Compare commits

...

172 Commits
4.3 ... 4.5

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
108 changed files with 4133 additions and 1152 deletions

View File

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

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
==================

33
README
View File

@@ -47,32 +47,7 @@ Frequently Asked Questions (FAQ).
The documentation is also available on the chrony web pages, accessible
through the URL
https://chrony.tuxfamily.org/
Where are new versions announced?
=================================
There is a low volume mailing list where new versions and other
important news relating to chrony are announced. You can join this list
by sending mail with the subject "subscribe" to
chrony-announce-request@chrony.tuxfamily.org
How can I get support for chrony?
=================================
There are two other mailing lists relating to chrony. chrony-users is a
discussion list for users, e.g. for questions about chrony configuration
and bug reports. chrony-dev is a more technical list for developers,
e.g. for submitting patches and discussing how new features should be
implemented. To subscribe to either of these lists, send a message with
the subject "subscribe" to
chrony-users-request@chrony.tuxfamily.org
or
chrony-dev-request@chrony.tuxfamily.org
as applicable.
https://chrony-project.org/
License
=======
@@ -108,6 +83,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr>
Dan Drown <dan-ntp@drown.org>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
@@ -121,6 +97,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
John Hasler <john@dhh.gt.org>
Tjalling Hattink <t.hattink@fugro.nl>
Liam Hatton <me@liamhatton.com>
Holger Hoffstätte <holger@applied-asynchrony.com>
Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com>
@@ -136,15 +113,19 @@ Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il>
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi>
Bill Unruh <unruh@physics.ubc.ca>
Luke Valenta <lvalenta@cloudflare.com>
Stephen Wadeley <swadeley@redhat.com>
Bernhard Weiss <lisnablagh@web.de>
Wolfgang Weisselberg <weissel@netcologne.de>

15
array.c
View File

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

View File

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

54
candm.h
View File

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

257
client.c
View File

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

View File

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

View File

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

103
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2021
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -144,6 +144,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
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
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.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields =
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
params.ext_fields = (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_MONO_ROOT ?
NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) {
@@ -1169,18 +1179,36 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS3);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
tx_message->reply = htons(RPY_SERVER_STATS4);
tx_message->data.server_stats.ntp_hits = UTI_Integer64HostToNetwork(report.ntp_hits);
tx_message->data.server_stats.nke_hits = UTI_Integer64HostToNetwork(report.nke_hits);
tx_message->data.server_stats.cmd_hits = UTI_Integer64HostToNetwork(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = UTI_Integer64HostToNetwork(report.ntp_drops);
tx_message->data.server_stats.nke_drops = UTI_Integer64HostToNetwork(report.nke_drops);
tx_message->data.server_stats.cmd_drops = UTI_Integer64HostToNetwork(report.cmd_drops);
tx_message->data.server_stats.log_drops = UTI_Integer64HostToNetwork(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits =
UTI_Integer64HostToNetwork(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits =
UTI_Integer64HostToNetwork(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps =
UTI_Integer64HostToNetwork(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds =
UTI_Integer64HostToNetwork(report.ntp_span_seconds);
tx_message->data.server_stats.ntp_daemon_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_daemon_rx_timestamps);
tx_message->data.server_stats.ntp_daemon_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_daemon_tx_timestamps);
tx_message->data.server_stats.ntp_kernel_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_kernel_rx_timestamps);
tx_message->data.server_stats.ntp_kernel_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_kernel_tx_timestamps);
tx_message->data.server_stats.ntp_hw_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_hw_rx_timestamps);
tx_message->data.server_stats.ntp_hw_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_hw_tx_timestamps);
memset(tx_message->data.server_stats.reserved, 0xff,
sizeof (tx_message->data.server_stats.reserved));
}
/* ================================================== */
@@ -1328,7 +1356,7 @@ handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
static uint16_t
convert_select_options(int options)
convert_sd_sel_options(int options)
{
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
@@ -1355,14 +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.authentication = report.authentication;
tx_message->data.select_data.leap = report.leap;
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
tx_message->data.select_data.conf_options = htons(convert_sd_sel_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_sd_sel_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
}
/* ================================================== */
static void
handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int mask, options;
uint32_t ref_id;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.modify_select_opts.address, &ip_addr);
ref_id = ntohl(rx_message->data.modify_select_opts.ref_id);
mask = ntohl(rx_message->data.modify_select_opts.mask);
options = convert_addsrc_select_options(ntohl(rx_message->data.modify_select_opts.options));
if (!SRC_ModifySelectOptions(&ip_addr, ref_id, options, mask))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
/* Read a packet and process it */
@@ -1515,6 +1561,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
}
if (allowed) {
LOG_SetContext(LOGC_Command);
switch(rx_command) {
case REQ_NULL:
/* Do nothing */
@@ -1757,11 +1805,17 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_reload_sources(&rx_message, &tx_message);
break;
case REQ_MODIFY_SELECTOPTS:
handle_modify_selectopts(&rx_message, &tx_message);
break;
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}
LOG_UnsetContext(LOGC_Command);
} else {
tx_message.status = htons(STT_UNAUTH);
}
@@ -1795,6 +1849,9 @@ CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status == ADF_BADSUBNET) {
return 0;
} else if (status == ADF_SUCCESS) {
LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
allow ? "Allowed" : "Denied", all ? " all" : "", "command",
UTI_IPSubnetToString(ip_addr, subnet_bits));
return 1;
} else {
return 0;

View File

@@ -44,7 +44,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
uint32_t ef_type;
int n;
int n, sel_option;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
@@ -101,14 +101,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0;
@@ -123,8 +115,11 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
return 0;
switch (ef_type) {
case NTP_EF_EXP1:
src->params.ext_fields |= NTP_EF_FLAG_EXP1;
case NTP_EF_EXP_MONO_ROOT:
src->params.ext_fields |= NTP_EF_FLAG_EXP_MONO_ROOT;
break;
case NTP_EF_EXP_NET_CORRECTION:
src->params.ext_fields |= NTP_EF_FLAG_EXP_NET_CORRECTION;
break;
default:
return 0;
@@ -187,6 +182,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
src->params.sel_options |= sel_option;
} else {
return 0;
}
@@ -197,6 +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
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;
}
/* ================================================== */
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 */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Get an NTP/refclock select option */
extern int CPS_GetSelectOption(char *option);
/* Parse a command to allow/deny access */
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
@@ -54,4 +57,7 @@ extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
/* Parse a refclock reference ID (returns number of characters) */
extern int CPS_ParseRefid(char *line, uint32_t *ref_id);
#endif /* GOT_CMDPARSE_H */

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. */
static char *user;
/* Address refresh interval */
static int refresh = 1209600; /* 2 weeks */
/* NTS server and client configuration */
static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL;
@@ -274,6 +277,9 @@ static int no_system_cert = 0;
/* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces;
/* Timeout for resuming reading from sockets waiting for HW TX timestamp */
static double hwts_timeout = 0.001;
/* PTP event port (disabled by default) */
static int ptp_port = 0;
@@ -602,6 +608,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_string(p, &hwclock_file);
} else if (!strcasecmp(command, "hwtimestamp")) {
parse_hwtimestamp(p);
} else if (!strcasecmp(command, "hwtstimeout")) {
parse_double(p, &hwts_timeout);
} else if (!strcasecmp(command, "include")) {
parse_include(p);
} else if (!strcasecmp(command, "initstepslew")) {
@@ -697,6 +705,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
} else if (!strcasecmp(command, "refclock")) {
parse_refclock(p);
} else if (!strcasecmp(command, "refresh")) {
parse_int(p, &refresh);
} else if (!strcasecmp(command, "reselectdist")) {
parse_double(p, &reselect_distance);
} else if (!strcasecmp(command, "rtcautotrim")) {
@@ -862,11 +872,10 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int local, max_lock_age, pps_forced, stratum, tai;
int local, max_lock_age, pps_forced, sel_option, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
unsigned char ref[5];
RefclockParameters *refclock;
poll = 4;
@@ -912,13 +921,11 @@ parse_refclock(char *line)
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "refid")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
if ((n = CPS_ParseRefid(line, &ref_id)) == 0)
break;
ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "lock")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
break;
lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
break;
@@ -971,18 +978,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
} else if (!strcasecmp(cmd, "noselect")) {
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
n = 0;
sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
n = 0;
sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "trust")) {
n = 0;
sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "require")) {
n = 0;
sel_options |= SRC_SELECT_REQUIRE;
sel_options |= sel_option;
} else {
other_parse_error("Invalid refclock option");
return;
@@ -1437,8 +1435,8 @@ static void
parse_hwtimestamp(char *line)
{
CNF_HwTsInterface *iface;
int n, maxpoll_set = 0;
char *p, filter[5];
int n;
if (!*line) {
command_parse_error();
@@ -1468,6 +1466,10 @@ parse_hwtimestamp(char *line)
} else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
} else if (!strcasecmp(p, "maxpoll")) {
if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
break;
maxpoll_set = 1;
} else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
break;
@@ -1503,6 +1505,9 @@ parse_hwtimestamp(char *line)
if (*p)
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;
if ((d = strcmp(sa->params.name, sb->params.name)) != 0)
return d;
if ((d = (int)(sa->type) - (int)(sb->type)) != 0)
if ((d = (int)sa->type - (int)sb->type) != 0)
return d;
if ((d = sa->pool - sb->pool) != 0)
if ((d = (int)sa->pool - (int)sb->pool) != 0)
return d;
if ((d = sa->params.port - sb->params.port) != 0)
if ((d = (int)sa->params.port - (int)sb->params.port) != 0)
return d;
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;
char buf[MAX_LINE_LENGTH];
NSR_Status s;
int d;
int d, pass;
prev_size = ARR_GetSize(ntp_source_ids);
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);
unresolved = 0;
LOG_SetContext(LOGC_SourceFile);
qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
for (i = j = 0; i < prev_size || j < new_size; ) {
if (i < prev_size && j < new_size)
d = compare_sources(&prev_sources[i], &new_sources[j]);
else
d = i < prev_size ? -1 : 1;
for (pass = 0; pass < 2; pass++) {
for (i = j = 0; i < prev_size || j < new_size; i += d <= 0, j += d >= 0) {
if (i < prev_size && j < new_size)
d = compare_sources(&prev_sources[i], &new_sources[j]);
else
d = i < prev_size ? -1 : 1;
if (d < 0) {
/* Remove the missing source */
if (prev_sources[i].params.name[0] != '\0')
/* Remove missing sources before adding others to avoid conflicts */
if (pass == 0 && d < 0 && prev_sources[i].params.name[0] != '\0') {
NSR_RemoveSourcesById(prev_ids[i]);
i++;
} else if (d > 0) {
/* Add a newly configured source */
source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, &new_ids[j]);
if (s == NSR_UnresolvedName) {
unresolved++;
} else if (s != NSR_Success) {
LOG(LOGS_ERR, "Could not add source %s", source->params.name);
/* Mark the source as not present */
source->params.name[0] = '\0';
}
j++;
} else {
/* Keep the existing source */
new_ids[j] = prev_ids[i];
i++, j++;
/* Add new sources */
if (pass == 1 && d > 0) {
source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, &new_ids[j]);
if (s == NSR_UnresolvedName) {
unresolved++;
} else if (s != NSR_Success) {
LOG(LOGS_ERR, "Could not add source %s : %s",
source->params.name, NSR_StatusToString(s));
/* Mark the source as not present */
source->params.name[0] = '\0';
}
}
/* Keep unchanged sources */
if (pass == 1 && d == 0)
new_ids[j] = prev_ids[i];
}
}
LOG_UnsetContext(LOGC_SourceFile);
for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name);
Free(prev_sources);
@@ -1782,6 +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
CNF_AddInitSources(void)
{
@@ -2500,6 +2524,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */
double
CNF_GetHwTsTimeout(void)
{
return hwts_timeout;
}
/* ================================================== */
int
CNF_GetPtpPort(void)
{
@@ -2508,6 +2540,14 @@ CNF_GetPtpPort(void)
/* ================================================== */
int
CNF_GetRefresh(void)
{
return refresh;
}
/* ================================================== */
char *
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_CheckReadOnlyAccess(void);
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
@@ -142,6 +144,7 @@ typedef enum {
typedef struct {
char *name;
int minpoll;
int maxpoll;
int min_samples;
int max_samples;
int nocrossts;
@@ -152,9 +155,12 @@ typedef struct {
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void);
extern int CNF_GetRefresh(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
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) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2021
# Copyright (C) Miroslav Lichvar 2009, 2012-2022
# Copyright (C) Stefan R. Filipek 2019
#
# =======================================================================
@@ -111,10 +111,10 @@ For better control, use the options below.
--without-editline Don't use editline even if it is available
--disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available
--without-gnutls Don't use gnutls even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-nts Disable NTS support
--without-gnutls Don't use gnutls even if it is available
--disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support
--disable-refclock Disable reference clock support
@@ -128,6 +128,7 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
--without-aes-gcm-siv Don't use AES-GCM-SIV for NTS even if it is available
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
@@ -244,6 +245,7 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_aes_gcm_siv=1
try_clock_gettime=1
try_arc4random=1
try_recvmmsg=1
@@ -345,6 +347,9 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
--without-aes-gcm-siv)
try_aes_gcm_siv=0
;;
--without-clock-gettime)
try_clock_gettime=0
;;
@@ -565,6 +570,13 @@ if [ "x$MYCFLAGS" = "x" ]; then
fi
fi
TESTCFLAGS="-fwrapv"
if test_code '-fwrapv' '' "$TESTCFLAGS" '' ''; then
GETDATE_CFLAGS="-fwrapv"
else
GETDATE_CFLAGS=""
fi
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
@@ -914,6 +926,28 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
fi
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3"
@@ -939,28 +973,6 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
fi
fi
fi
EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK"
@@ -979,20 +991,34 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
then
if test_code 'SIV in nettle' \
if [ $try_nettle = "1" ] && test_code 'AES-SIV-CMAC in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in nettle' \
'nettle/siv-gcm.h' "" "$LIBS" \
'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
(void *)4);'
then
add_def HAVE_NETTLE_SIV_GCM
fi
else
if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in gnutls' \
'gnutls/crypto.h' "$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV_GCM,
(void *)2);'
then
add_def HAVE_GNUTLS_SIV_GCM
fi
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
@@ -1110,6 +1136,7 @@ do
s%@CFLAGS@%${MYCFLAGS}%;\
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@GETDATE_CFLAGS@%${GETDATE_CFLAGS}%;\
s%@LIBS@%${LIBS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2022
// Copyright (C) Miroslav Lichvar 2009-2023
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
@@ -72,9 +72,7 @@ newly resolved address when the server becomes unreachable (i.e. no valid
response to last 8 requests), unsynchronised, a falseticker (i.e. does not
agree with a majority of other sources), or the root distance is too large (the
limit can be configured by the <<maxdistance,*maxdistance*>> directive). The
automatic replacement happens at most once per 30 minutes. It can also be
triggered manually for all sources by the <<chronyc.adoc#refresh,*refresh*>>
command in *chronyc*.
automatic replacement happens at most once per 30 minutes.
+
This directive can be used multiple times to specify multiple servers.
+
@@ -324,16 +322,27 @@ server implementations do not respond to requests containing an unknown
extension field (*chronyd* as a server responded to such requests since
version 2.0).
+
The following extension field can be enabled by this option:
This option can be used multiple times to enable multiple extension fields.
+
The following extension fields are supported:
+
_F323_::::
This is an experimental extension field for some improvements that were
An experimental extension field to enable several improvements that were
proposed for the next version of the NTP protocol (NTPv5). The field contains
root delay and dispersion in higher resolution and a monotonic receive
timestamp, which enables a frequency transfer between the server and client. It
can significantly improve stability of the synchronization. Generally, it
should be expected to work only between servers and clients running the same
version of *chronyd*.
timestamp, which enables a frequency transfer between the server and client to
significantly improve stability of the synchronisation. This field should be
enabled only for servers known to be running *chronyd* version 4.2 or later.
_F324_::::
An experimental extension field to enable the use of the Precision Time
Protocol (PTP) correction field in NTP-over-PTP messages updated by one-step
end-to-end transparent clocks in network switches and routers to significantly
improve accuracy and stability of the synchronisation. NTP-over-PTP can be
enabled by the <<ptpport,*ptpport*>> directive and setting the *port* option to
the PTP port. The corrections are applied only to NTP measurements with HW
timestamps (enabled by the <<hwtimestamp,*hwtimestamp*>> directive). This
field should be enabled only for servers known to be running *chronyd* version
4.5 or later.
{blank}:::
[[pool]]*pool* _name_ [_option_]...::
@@ -420,7 +429,7 @@ error. *chronyd* then enters its normal operating mode.
An example of the use of the directive is:
+
----
initstepslew 30 foo.example.net bar.example.net baz.example.net
initstepslew 30 ntp1.example.net ntp2.example.net ntp3.example.net
----
+
where 3 NTP servers are used to make the measurement. The _30_ indicates that
@@ -473,16 +482,41 @@ instead.
Examples:
+
----
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps0 lock NMEA refid GPS1
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
----
+
*SOCK*:::
Unix domain socket driver. This driver uses a datagram socket to receive
samples from another application running on the system. The parameter is the
path to the socket, which *chronyd* will create on start. The format of the
messages is described in the _refclock_sock.c_ file in the chrony source code.
+
An application which supports the SOCK protocol is the *gpsd* daemon. It can
provide accurate measurements using the receiver's PPS signal, and since
version 3.25 also (much less accurate) measurements based on the timing of
serial data (e.g. NMEA), which can be useful when the receiver does not provide
a PPS signal, or it cannot be connected to the computer. The paths where *gpsd*
expects the sockets to be created by *chronyd* are described in the *gpsd(8)*
man page. Note that *gpsd* needs to be started after *chronyd* in order to
connect to the socket.
+
Examples:
+
----
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS1 poll 2 filter 4
refclock SOCK /var/run/chrony.clk.ttyUSB0.sock refid GPS2 offset 0.2 delay 0.1
----
+
*SHM*:::
NTP shared memory driver. This driver uses a shared memory segment to receive
samples from another process (e.g. *gpsd*). The parameter is the number of the
shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
supports the following option:
NTP shared memory driver. This driver implements the protocol of the *ntpd*
driver type 28. It is functionally similar to the SOCK driver, but uses a
shared memory segment instead of a socket. The parameter is the unit number,
typically a small number like 0, 1, 2, or 3, from which is derived the key of
the memory segment as 0x4e545030 + unit.
+
The driver supports the following option:
+
*perm*=_mode_::::
This option specifies the permissions of the shared memory segment created by
@@ -490,6 +524,16 @@ This option specifies the permissions of the shared memory segment created by
(read-write access for owner only).
{blank}:::
+
Unlike with the SOCK driver, there is no prescribed order of starting *chronyd*
and the program providing measurements. Both are expected to create the memory
segment if it does not exist. *chronyd* will attach to an existing segment even
if it has a different owner than root or different permissions than the
permissions specified by the *perm* option. The segment needs to be created
before untrusted applications or users can execute code to prevent an attacker
from feeding *chronyd* with false measurements. The owner and permissions of
the segment can be verified with the *ipcs -m* command. For this reason, the
SHM driver is deprecated in favor of SOCK.
+
Examples:
+
----
@@ -497,23 +541,6 @@ refclock SHM 0 poll 3 refid GPS1
refclock SHM 1:perm=0644 refid GPS2
----
+
*SOCK*:::
Unix domain socket driver. It is similar to the SHM driver, but samples are
received from a Unix domain socket instead of shared memory and the messages
have a different format. The parameter is the path to the socket, which
*chronyd* creates on start. An advantage over the SHM driver is that SOCK does
not require polling and it can receive PPS samples with incomplete time. The
format of the messages is described in the _refclock_sock.c_ file in the chrony
source code.
+
An application which supports the SOCK protocol is the *gpsd* daemon. The path
where *gpsd* expects the socket to be created is described in the *gpsd(8)* man
page. For example:
+
----
refclock SOCK /var/run/chrony.ttyS0.sock
----
+
*PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of
the PTP clock which should be used as a time source. If the clock is kept in
@@ -624,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
remaining samples. If the length is 4 or more, at least 4 samples have to be
collected between polls. For lengths below 4, the filter has to be full. The
default is 64.
default is 64. With drivers that perform their own polling (PPS, PHC, SHM), the
maximum value is adjusted to the number of driver polls per source poll, i.e.
2^(_poll_ - _dpoll_).
*prefer*:::
Prefer this source over sources without the prefer option.
*noselect*:::
@@ -809,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
seconds) in order to refresh the keys authenticating NTP packets. The default
value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years).
+
The interval must be longer than polling intervals of all configured NTP
sources using NTS, otherwise the source with a longer polling interval will
refresh the keys on each poll and no NTP packets will be exchanged.
[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
This directive specifies a file or directory containing certificates (in the
PEM format) of trusted certificate authorities (CA) which can be used to
verify certificates of NTS servers.
This directive specifies a file or directory containing trusted certificates
(in the PEM format) which are needed to verify certificates of NTS-KE servers,
e.g. certificates of trusted certificate authorities (CA) or self-signed
certificates of the servers.
+
The optional _set-ID_ argument is a number in the range 0 through 2^32-1, which
selects the set of certificates where certificates from the specified file
@@ -833,10 +867,10 @@ they change (e.g. after a renewal).
An example is:
+
----
ntstrustedcerts /etc/pki/nts/foo.crt
ntstrustedcerts 1 /etc/pki/nts/bar.crt
ntstrustedcerts 1 /etc/pki/nts/baz.crt
ntstrustedcerts 2 /etc/pki/nts/qux.crt
ntstrustedcerts /etc/pki/nts/ca1.example.net.crt
ntstrustedcerts 1 /etc/pki/nts/ca2.example.net.crt
ntstrustedcerts 1 /etc/pki/nts/ca3.example.net.crt
ntstrustedcerts 2 /etc/pki/nts/ntp2.example.net.crt
----
[[nosystemcert]]*nosystemcert*::
@@ -862,6 +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
with correct time.
[[refresh]]*refresh* _interval_::
This directive specifies the interval (in seconds) between refreshing IP
addresses of NTP sources specified by hostname. If the hostname no longer
resolves to the currently used address, it will be replaced with one of the new
addresses to avoid using a server which is no longer intended for service, even
if it is still responding correctly and would not be replaced as unreachable.
Only one source is refreshed at a time. The default value is 1209600 (2 weeks)
and the maximum value is 2^31-1 (68 years). A value of 0 disables the periodic
refreshment.
+
The <<chronyc.adoc#refresh,*refresh*>> command can be used to refresh all
sources immediately.
=== Source selection
[[authselectmode]]*authselectmode* _mode_::
@@ -920,20 +967,20 @@ before 4.0.
As an example, the following configuration using the default *mix* mode:
+
----
server foo.example.net nts
server bar.example.net nts
server baz.example.net
refclock SHM 0
server ntp1.example.net nts
server ntp2.example.net nts
server ntp3.example.net
refclock SOCK /var/run/chrony.ttyS0.sock
----
+
is equivalent to the following configuration using the *ignore* mode:
+
----
authselectmode ignore
server foo.example.net nts require trust
server bar.example.net nts require trust
server baz.example.net
refclock SHM 0 require trust
server ntp1.example.net nts require trust
server ntp2.example.net nts require trust
server ntp3.example.net
refclock /var/run/chrony.ttyS0.sock require trust
----
[[combinelimit]]*combinelimit* _limit_::
@@ -1302,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
gain or loss rate, and the error bound on the estimate. When a new estimate is
generated following another measurement from one of the sources, a weighted
combination algorithm is used to update the master estimate. So if *chronyd*
has an existing highly-reliable master estimate and a new estimate is generated
which has large error bounds, the existing master estimate will dominate in the
new master estimate.
combination algorithm is used to update the existing estimate. If it has
significantly smaller error bounds than the new estimate, the existing estimate
will dominate in the new combined value.
[[maxslewrate]]*maxslewrate* _rate-in-ppm_::
The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
@@ -1735,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
be periodically copied from another server running *chronyd* (which does
not have *ntsrotate* set to 0) in order to have one or more servers dedicated
to NTS-KE. The NTS-KE servers need to be configured with the
to NTS-KE. The file includes the subsequent key to which the NTS-KE server will
switch on the next rotation, i.e. the process copying and reloading the file
does not need to be timed precisely (it can be delayed by up to one rotation
interval). The NTS-KE servers need to be configured with the
<<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
server.
+
@@ -1939,8 +1988,9 @@ all* directive.
[[cmdport]]*cmdport* _port_::
The *cmdport* directive allows the port that is used for run-time monitoring
(via the *chronyc* program) to be altered from its default (323). If set to 0,
*chronyd* will not open the port, this is useful to disable *chronyc*
access from the Internet. (It does not disable the Unix domain command socket.)
*chronyd* will not open the port, which disables remote *chronyc* access (with
a non-default *bindcmdaddress*) and local access for unprivileged users. It
does not disable the Unix domain command socket.
+
An example shows the syntax:
+
@@ -1949,7 +1999,7 @@ cmdport 257
----
+
This would make *chronyd* use UDP 257 as its command port. (*chronyc* would
need to be run with the *-p 257* switch to inter-operate correctly.)
need to be run with the *-p 257* option to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is
@@ -2218,6 +2268,13 @@ Used for synchronisation of the local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
. Current effective selection options of the source. which can be different
from the configured options due to the authentication selection mode
(configured by the <<authselectmode,*authselectmode*>> directive). [-----]
* _N_ indicates the *noselect* option.
* _P_ indicates the *prefer* option.
* _T_ indicates the *trust* option.
* _R_ indicates the *require* option.
. Reachability register printed as an octal number. The register has 8 bits and
is updated on every received or missed packet from the source. A value of 377
indicates that a valid reply was received for all from the last eight
@@ -2503,10 +2560,13 @@ physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
feature is available on Linux 5.14 and newer.
+
If the kernel supports software timestamping, it will be enabled for all
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
indicated in the _measurements.log_ file if enabled by the <<log,*log
measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
*chronyc*.
interfaces automatically.
+
The source of timestamps (i.e. hardware, kernel, or daemon) is indicated on the
client side in the _measurements.log_ file (if enabled by the <<log,*log*>>
directive) and the <<chronyc.adoc#ntpdata,*ntpdata*>> report. On the server
side, the number of served timestamps from each source is provided in the
<<chronyc.adoc#serverstats,*serverstats*>> report.
+
This directive can be used multiple times to enable HW timestamping on multiple
interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
@@ -2516,10 +2576,15 @@ The *hwtimestamp* directive has the following options:
+
*minpoll* _poll_:::
This option specifies the minimum interval between readings of the NIC clock.
It's defined as a power of two. It should correspond to the minimum polling
It's defined as a power of 2. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
of a second).
clients. The default value is 0 (1 second), the minimum value is -6 (1/64th
of a second), and the maximum value is 20 (about 12 days).
*maxpoll* _poll_:::
This option specifies the maximum interval between readings of the NIC clock,
as a power of 2. The default value is *minpoll* + 1, i.e. 1 (2 seconds) with
the default *minpoll* of 0. The minimum and maximum values are the same as with
the *minpoll* option.
*minsamples* _samples_:::
This option specifies the minimum number of readings kept for tracking of the
NIC clock. The default value is 2.
@@ -2572,6 +2637,27 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
----
[[hwtstimeout]]*hwtstimeout* _timeout_::
If hardware timestamping is used with a close NTP server, or the NIC or its
driver is slow in providing the transmit timestamp of NTP requests, a response
from the server can be received before the transmit timestamp of the request.
To avoid calculating the offset with a less accurate transmit timestamp,
*chronyd* can save the response for later processing and wait for the hardware
transmit timestamp. There is no guarantee that the timestamp will be provided
(NICs typically have a limited rate of transmit timestamping). This directive
configures how long should *chronyd* wait for the timestamp after receiving a
valid response from the server. If a second valid response is received from the
server while waiting for the timestamp, they will be both processed
immediately.
+
The default value is 0.001 seconds, which should be sufficient with most
hardware. If you frequently see kernel transmit timestamps in the
_measurements.log_ file or <<chronyc.adoc#ntpdata,*ntpdata*>> report, and it is
not a server handling a high rate of requests in the interleaved mode on the
same interface (which would compete with timestamping of the server's own
requests), increasing the timeout to 0.01 or possibly even longer might help.
Note that the maximum timeout is limited by the NTP polling interval.
[[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric
keys which are shared between NTP servers and clients, or peers, in order to
@@ -2655,24 +2741,27 @@ pidfile /run/chronyd.pid
The *ptpport* directive enables *chronyd* to send and receive NTP messages
contained in PTP event messages (NTP-over-PTP) to enable hardware timestamping
on NICs which cannot timestamp NTP packets, but can timestamp unicast PTP
packets. The port recognized by the NICs is 319 (PTP event port). The default
value is 0 (disabled).
packets, and also use corrections provided by PTP one-step end-to-end
transparent clocks in network switches and routers. The port recognized by the
NICs and PTP transparent clocks is 319 (PTP event port). The default value is 0
(disabled).
+
The NTP-over-PTP support is experimental. The protocol and configuration can
change in future. It should be used only in local networks and expected to work
only between servers and clients running the same version of *chronyd*.
change in future. It should be used only in local networks.
+
The PTP port will be open even if *chronyd* is not configured to operate as a
server or client. The directive does not change the default protocol of
specified NTP sources. Each NTP source that should use NTP-over-PTP needs to
be specified with the *port* option set to the PTP port. To actually enable
hardware timestamping on NICs which can timestamp PTP packets only, the
*rxfilter* option of the *hwtimestamp* directive needs to be set to _ptp_.
*rxfilter* option of the *hwtimestamp* directive needs to be set to _ptp_. The
extension field _F324_ needs to be enabled to use the corrections provided by
the PTP transparent clocks.
+
An example of client configuration is:
+
----
server foo.example.net minpoll 0 maxpoll 0 xleave port 319
server ntp1.example.net minpoll 0 maxpoll 0 xleave port 319 extfield F324
hwtimestamp * rxfilter ptp
ptpport 319
----
@@ -2733,13 +2822,13 @@ the following methods:
facilities.
* Use public servers from the https://www.pool.ntp.org/[pool.ntp.org] project.
Assuming that your NTP servers are called _foo.example.net_, _bar.example.net_
and _baz.example.net_, your _chrony.conf_ file could contain as a minimum:
Assuming that your NTP servers are called _ntp1.example.net_, _ntp2.example.net_
and _ntp3.example.net_, your _chrony.conf_ file could contain as a minimum:
----
server foo.example.net
server bar.example.net
server baz.example.net
server ntp1.example.net
server ntp2.example.net
server ntp3.example.net
----
However, you will probably want to include some of the other directives. The
@@ -2750,9 +2839,9 @@ synchronisation. The smallest useful configuration file would look something
like:
----
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
server ntp1.example.net iburst
server ntp2.example.net iburst
server ntp3.example.net iburst
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
rtcsync
@@ -2776,9 +2865,9 @@ option will enable a secure synchronisation to the servers. The configuration
file could look like:
----
server foo.example.net iburst nts
server bar.example.net iburst nts
server baz.example.net iburst nts
server ntp1.example.net iburst nts
server ntp2.example.net iburst nts
server ntp3.example.net iburst nts
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
rtcsync
@@ -2792,14 +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
they are inaccessible.
Again, assuming that your NTP servers are called _foo.example.net_,
_bar.example.net_ and _baz.example.net_, your _chrony.conf_ file would now
Again, assuming that your NTP servers are called _ntp1.example.net_,
_ntp2.example.net_ and _ntp3.example.net_, your _chrony.conf_ file would now
contain:
----
server foo.example.net offline
server bar.example.net offline
server baz.example.net offline
server ntp1.example.net offline
server ntp2.example.net offline
server ntp3.example.net offline
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
rtcsync
@@ -2983,9 +3072,9 @@ configuration files are shown.
For the _chrony.conf_ file, the following can be used as an example.
----
server foo.example.net maxdelay 0.4 offline
server bar.example.net maxdelay 0.4 offline
server baz.example.net maxdelay 0.4 offline
server ntp1.example.net maxdelay 0.4 offline
server ntp2.example.net maxdelay 0.4 offline
server ntp3.example.net maxdelay 0.4 offline
logdir /var/log/chrony
log statistics measurements tracking
driftfile @CHRONYVARDIR@/drift
@@ -3044,10 +3133,10 @@ configuration).
The configuration file could look like:
----
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
server qux.example.net iburst
server ntp1.example.net iburst
server ntp2.example.net iburst
server ntp3.example.net iburst
server ntp4.example.net iburst
makestep 1.0 3
rtcsync
allow
@@ -3064,7 +3153,7 @@ dumpdir @CHRONYRUNDIR@
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
https://chrony-project.org/.
== AUTHORS

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

5
keys.c
View File

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

View File

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

View File

@@ -100,6 +100,20 @@ extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Get the minimum severity */
extern LOG_Severity LOG_GetMinSeverity(void);
/* Flags for info messages that should be logged only in specific contexts */
typedef enum {
LOGC_Command = 1,
LOGC_SourceFile = 2,
} LOG_Context;
/* Modify current contexts */
extern void LOG_SetContext(LOG_Context context);
extern void LOG_UnsetContext(LOG_Context context);
/* Get severity depending on the current active contexts: INFO if they contain
at least one of the specified contexts, DEBUG otherwise */
extern LOG_Severity LOG_GetContextSeverity(LOG_Context contexts);
/* Set a prefix for debug messages */
extern void LOG_SetDebugPrefix(const char *prefix);

20
main.c
View File

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

15
md5.c
View File

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

View File

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

26
ntp.h
View File

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

View File

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

View File

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

View File

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

View File

@@ -39,6 +39,9 @@ extern void NIO_Initialise(void);
/* Function to finalise the module */
extern void NIO_Finalise(void);
/* Function to check if HW timestamping is enabled on any interface */
extern int NIO_IsHwTsEnabled(void);
/* Function to obtain a socket for sending client packets */
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr);
@@ -61,7 +64,7 @@ extern int NIO_IsServerSocketOpen(void);
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
/* Function to unwrap an NTP message from non-native transport (e.g. PTP) */
extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd);
extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction);
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -129,6 +129,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
};
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 */
0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS3 */
0, /* SERVER_STATS3 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
};
/* ================================================== */

7
ptp.h
View File

@@ -44,7 +44,12 @@ typedef struct {
uint8_t domain;
uint8_t min_sdoid;
uint16_t flags;
uint8_t rest[26];
uint8_t correction[8];
uint8_t msg_specific[4];
uint8_t port_id[10];
uint16_t sequence_id;
uint8_t control;
int8_t interval;
} PTP_Header;
typedef struct {

View File

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

View File

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

View File

@@ -1329,6 +1329,7 @@ void
REF_ModifyMaxupdateskew(double new_max_update_skew)
{
max_update_skew = new_max_update_skew * 1.0e-6;
LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
}
/* ================================================== */
@@ -1338,6 +1339,7 @@ REF_ModifyMakestep(int limit, double threshold)
{
make_step_limit = limit;
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_distance = distance;
local_orphan = !!orphan;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}
/* ================================================== */
@@ -1357,6 +1360,7 @@ void
REF_DisableLocal(void)
{
enable_local_stratum = 0;
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
}
/* ================================================== */

View File

@@ -109,17 +109,23 @@ typedef struct {
} RPT_ClientAccessByIndex_Report;
typedef struct {
uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops;
uint32_t log_drops;
uint32_t ntp_auth_hits;
uint32_t ntp_interleaved_hits;
uint32_t ntp_timestamps;
uint32_t ntp_span_seconds;
uint64_t ntp_hits;
uint64_t nke_hits;
uint64_t cmd_hits;
uint64_t ntp_drops;
uint64_t nke_drops;
uint64_t cmd_drops;
uint64_t log_drops;
uint64_t ntp_auth_hits;
uint64_t ntp_interleaved_hits;
uint64_t ntp_timestamps;
uint64_t ntp_span_seconds;
uint64_t ntp_daemon_rx_timestamps;
uint64_t ntp_daemon_tx_timestamps;
uint64_t ntp_kernel_rx_timestamps;
uint64_t ntp_kernel_tx_timestamps;
uint64_t ntp_hw_rx_timestamps;
uint64_t ntp_hw_tx_timestamps;
} RPT_ServerStatsReport;
typedef struct {

16
sched.c
View File

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

View File

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

4
siv.h
View File

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

View File

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

View File

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

View File

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

183
socket.c
View File

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

View File

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

220
sources.c
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -91,6 +91,18 @@ check_chronyd_exit || test_fail
check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
|| test_fail
chronyc_options="-c -e"
chronyc_conf="sources"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^#,.,SHM0.*
\^,.,192\.168\.123\.1.*
\^,.,192\.168\.123\.2.*
\.$" \
|| test_fail
chronyc_options=""
server_strata=0
chronyc_start=0.5
@@ -102,7 +114,7 @@ limit=1
for chronyc_conf in \
"accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324" \
"add server node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \
@@ -164,6 +176,9 @@ for chronyc_conf in \
"reselectdist 1e-3" \
"reset sources" \
"selectdata" \
"selectopts 1.2.3.4 -noselect +trust +require +prefer" \
"selectopts ID#0000000001 +prefer" \
"selectopts PPS0 +prefer" \
"settime 16:30" \
"settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \
@@ -245,7 +260,13 @@ NTS-KE connections dropped : 0
Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
NTP timestamp span : 0$" || test_fail
NTP timestamp span : 0
NTP daemon RX timestamps : 0
NTP daemon TX timestamps : 1
NTP kernel RX timestamps : 1
NTP kernel TX timestamps : 0
NTP hardware RX timestamps : 0
NTP hardware TX timestamps : 0$" || test_fail
chronyc_conf="
deny all
@@ -327,6 +348,10 @@ maxupdateskew 192.168.123.1 10.0
minpoll 192.168.123.1 3
minstratum 192.168.123.1 1
polltarget 192.168.123.1 10
selectopts 192.168.123.1 +trust +prefer -require
selectdata
selectopts 192.168.123.1 +noselect -prefer -trust +require
selectdata
delete 192.168.123.1"
run_test || test_fail
@@ -345,6 +370,14 @@ check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
200 OK
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N \-PT\-\- \-PT\-\- 0 1\.0 \+0ns \+0ns \?
200 OK
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N N\-\-R\- N\-\-R\- 0 1\.0 \+0ns \+0ns \?
200 OK$" || test_fail
chronyc_conf="

View File

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

View File

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

View File

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

View File

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

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_start "experimental extension field"
test_start "mono+root extension field"
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_freq_offset=1e-4
default_base_delay=1e-4
default_delay_correction=""
default_jitter=1e-4
default_jitter_asymmetry=0.0
default_wander=1e-9
@@ -78,6 +79,7 @@ default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
default_cmdmon_unix=1
default_pcap_dumps=0
default_dns=0
# Initialize test settings from their defaults
@@ -459,6 +461,10 @@ run_test() {
for j in $(seq 1 $nodes); do
echo "node${i}_delay${j} = $(get_delay_expr up)"
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 > tmp/conf
@@ -469,6 +475,9 @@ run_test() {
for i in $(seq 1 $n); do
test_message 2 0 "starting node $node:"
[ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
if [ $stratum -eq 1 ]; then
step=$server_step
start=$server_start
@@ -509,6 +518,8 @@ run_test() {
for i in $(seq 1 $[$nodes - $node + 1]); do
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")
if [ $cmdmon_unix -ne 0 ]; then
options+=" -h /clknetsim/unix/$[$node - $clients]:1"

View File

@@ -42,6 +42,7 @@ for command in \
"reselect" \
"reselectdist 1e-3" \
"reset sources" \
"selectopts $server -noselect +trust +prefer +require" \
"smoothtime reset" \
"smoothtime activate" \
; do
@@ -101,7 +102,7 @@ Total good RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail
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
check_chronyc_output "^NTP packets received : [0-9]+
@@ -114,7 +115,13 @@ NTS-KE connections dropped : 0
Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
NTP timestamp span : 0$"|| test_fail
NTP timestamp span : 0
NTP daemon RX timestamps : 0
NTP daemon TX timestamps : [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
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.2" > $TEST_DIR/conf5.d/2.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
@@ -46,12 +48,16 @@ check_chronyc_output "^[^=]*
.. 127\.123\.1\.2 [^^]*
.. 127\.123\.5\.1 [^^]*
.. 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
echo "server 127.123.5.2 minpoll 7" > $TEST_DIR/conf5.d/2.sources
echo > $TEST_DIR/conf5.d/3.sources
echo "server 127.123.5.4" > $TEST_DIR/conf5.d/4.sources
echo "server 127.123.5.2 minpoll 5" > $TEST_DIR/conf5.d/2.sources
echo "server 127.123.5.3 minpoll 7" > $TEST_DIR/conf5.d/3.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
@@ -66,9 +72,12 @@ check_chronyc_output "^[^=]*
.. 127\.123\.2\.3 [^^]*
.. 127\.123\.4\.4 [^^]*
.. 127\.123\.1\.2 *[05] 6 [^^]*
.. 127\.123\.5\.2 *[05] 7 [^^]*
.. 127\.123\.5\.4 [^^]*$" || test_fail
.. 127\.123\.5\.5 [^^]*
.. 127\.123\.5\.2 *[05] 5 [^^]*
.. 127\.123\.5\.3 *[05] 7 [^^]*
.. 127\.123\.5\.6 [^^]*$" || test_fail
stop_chronyd || test_fail
check_chronyd_message_count "Could not add source" 1 1 || test_fail
test_pass

View File

@@ -43,7 +43,7 @@ wait_for_sync || test_fail
run_chronyc "authdata" || test_fail
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
check_chronyd_messages || test_fail
@@ -57,7 +57,7 @@ wait_for_sync || test_fail
run_chronyc "authdata" || test_fail
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
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"
for level in "-1" "1" "-2" "2"; do
for level in 1 2 -1 -2; do
test_message 1 1 "level $level:"
for test in 0[0-8][0-9]-*[^_]; do
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"
for level in "-1" "1" "-2" "2"; do
for level in 1 2 -1 -2; do
test_message 1 1 "level $level:"
for test in 1[0-8][0-9]-*[^_]; do
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 || \
test_skip "$user cannot access $TEST_DIR"
rm "$TEST_DIR/test"
else
chown 0:0 "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
fi
echo "Testing $*:"
@@ -322,7 +324,7 @@ check_chronyd_messages() {
([ "$clock_control" -ne 0 ] || grep -q 'Disabled control of system clock' "$logfile") && \
([ "$minimal_config" -ne 0 ] || grep -q 'Frequency .* read from' "$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" && \
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;
uint32_t index2, prev_first, prev_size;
NTP_Timestamp_Source ts_src, ts_src2;
struct timespec ts, ts2;
int i, j, k, index, shift;
CLG_Service s;
@@ -95,7 +96,7 @@ test_unit(void)
TEST_CHECK(!ntp_ts_map.timestamps);
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.first == 0);
TEST_CHECK(ntp_ts_map.size == 0);
@@ -132,8 +133,10 @@ test_unit(void)
UTI_ZeroTimespec(&ts);
}
ts_src = random() % (MAX_NTP_TS + 1);
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) {
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(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_IsZeroTimespec(&ts) || ts_src == ts_src2);
for (k = random() % 4; k > 0; k--) {
index2 = random() % ntp_ts_map.size;
int64_to_ntp64(get_ntp_tss(index2)->rx_ts, &ntp_ts);
if (random() % 2)
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
@@ -165,10 +169,12 @@ test_unit(void)
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(ts_src == ts_src2);
if (random() % 2) {
uint16_t prev_epoch = ntp_ts_map.slew_epoch;
@@ -181,20 +187,20 @@ test_unit(void)
index = random() % (ntp_ts_map.size - 1);
if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) {
int64_to_ntp64(get_ntp_tss(index)->rx_ts + 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
if (random() % 2) {
int64_to_ntp64(get_ntp_tss(0)->rx_ts - 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
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);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
@@ -207,7 +213,7 @@ test_unit(void)
while (ntp_ts_map.size < ntp_ts_map.max_size) {
ts64 += get_random64() >> (shift + 8);
int64_to_ntp64(ts64, &ntp_ts);
CLG_SaveNtpTimestamps(&ntp_ts, NULL);
CLG_SaveNtpTimestamps(&ntp_ts, NULL, 0);
if (ntp_ts_map.cached_index + NTPTS_INSERT_LIMIT < ntp_ts_map.size)
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_first_ts64 = get_ntp_tss(0)->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));
@@ -259,13 +265,13 @@ test_unit(void)
if (random() % 10 == 0) {
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--) {
ts64 = get_random64() >> shift;
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);
TEST_CHECK(ntp_ts_map.size == 0);
TEST_CHECK(ntp_ts_map.cached_rx_ts == 0ULL);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
}
}

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
* 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 int req_length, res_length;
#define NIO_IsHwTsEnabled() 1
#define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0)
#define NIO_CloseServerSocket(fd) assert(fd == 100)
#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
@@ -80,7 +81,7 @@ get_random_key_id(void)
}
static void
send_request(NCR_Instance inst)
send_request(NCR_Instance inst, int late_hwts)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
@@ -98,12 +99,18 @@ send_request(NCR_Instance inst)
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 101;
zero_local_timestamp(&local_ts);
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL;
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
@@ -115,8 +122,8 @@ process_request(NTP_Remote_Address *remote_addr)
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 100;
zero_local_timestamp(&local_ts);
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL;
res_length = 0;
@@ -267,7 +274,8 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
}
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_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.if_index = INVALID_IF_INDEX;
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.err = 0.0;
local_ts.source = NTP_TS_KERNEL;
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);
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)
TEST_CHECK(ret);
else if (!good)
@@ -327,7 +348,7 @@ process_replay(NCR_Instance inst, NTP_Packet *packet_queue,
do {
res_buffer = packet_queue[random() % queue_length];
} 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);
}
@@ -392,7 +413,7 @@ test_unit(void)
"local",
"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;
NTP_Remote_Address remote_addr;
NCR_Instance inst1, inst2;
@@ -439,6 +460,8 @@ test_unit(void)
for (j = 0; j < 50; 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 ||
inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
authenticated = random() % 2;
@@ -454,35 +477,35 @@ test_unit(void)
(int)source.params.authkey, source.params.version,
interleaved, authenticated, valid, updated, has_updated);
send_request(inst1);
send_request(inst1, late_hwts);
send_response(interleaved, authenticated, 1, 0, 1);
DEBUG_LOG("response 1");
proc_response(inst1, 0, 0, 0, updated);
proc_response(inst1, 0, 0, 0, updated, 0);
if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0);
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);
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");
proc_response(inst1, 0, 0, 0, 0);
proc_response(inst1, 0, 0, 0, 0, 0);
advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1);
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);
send_response(interleaved, authenticated, 1, 1, 1);
DEBUG_LOG("response 6");
proc_response(inst1, 0, 0, valid && updated, updated);
proc_response(inst1, 0, 0, valid && updated, updated, 0);
}
NCR_DestroyInstance(inst1);
@@ -494,12 +517,12 @@ test_unit(void)
for (j = 0; j < 20; j++) {
DEBUG_LOG("server test iteration %d/%d", i, j);
send_request(inst1);
send_request(inst1, 0);
process_request(&remote_addr);
proc_response(inst1,
!source.params.interleaved || source.params.version != 4 ||
inst1->mode == MODE_ACTIVE || j != 2,
1, 1, 0);
1, 1, 0, 0);
advance_time(1 << inst1->local_poll);
}
@@ -515,7 +538,9 @@ test_unit(void)
for (j = 0; j < 20; 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;
assert(!res_length || res_length == req_length);
res_length = req_length;
@@ -523,7 +548,7 @@ test_unit(void)
TEST_CHECK(inst1->valid_timestamps == (j > 0));
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;
@@ -536,14 +561,14 @@ test_unit(void)
advance_time(1 << (source.params.minpoll - 1));
send_request(inst2);
send_request(inst2, 0);
res_buffer = req_buffer;
assert(res_length == req_length);
TEST_CHECK(inst2->valid_timestamps == (j > 0));
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;

View File

@@ -28,6 +28,7 @@
#include <nameserv_async.h>
#include <ntp_core.h>
#include <ntp_io.h>
#include <sched.h>
static char *requested_name = 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)
#define NCR_ProcessRxKnown(remote_addr, local_addr, ts, msg, len) (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,
int ntp_only);
static double get_mono_time(void);
#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(strlen(NSR_StatusToString(status)) > 0);
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);
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);
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)
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
test_unit(void)
{

View File

@@ -64,9 +64,12 @@ prepare_response(NKSN_Instance session, int valid)
if (index != 5) {
if (index == 6)
data[0] = htons(AEAD_AES_SIV_CMAC_256 + random() % 10 + 1);
do {
data[0] = htons(random() % 100);
} while (SIV_GetKeyLength(ntohs(data[0])) > 0);
else
data[0] = htons(AEAD_AES_SIV_CMAC_256);
data[0] = htons(random() % 2 && SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256);
if (index == 7)
length = 3 + random() % 10;
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));

View File

@@ -75,7 +75,8 @@ prepare_request(NKSN_Instance session, int valid)
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
if (index != 4) {
data[0] = htons(AEAD_AES_SIV_CMAC_256);
data[0] = htons(random() % 2 && SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256);
if (index == 5)
length = 0;
else if (index == 6)
@@ -138,7 +139,7 @@ test_unit(void)
NKSN_Instance session;
NKE_Context context, context2;
NKE_Cookie cookie;
int i, valid, l;
int i, j, valid, l;
uint32_t sum, sum2;
char conf[][100] = {
@@ -199,7 +200,9 @@ test_unit(void)
save_keys();
for (i = 0, sum = 0; i < MAX_SERVER_KEYS; i++) {
sum += server_keys[i].id + server_keys[i].key[0];
sum += server_keys[i].id;
for (j = 0; j < sizeof (server_keys[i].key); j++)
sum += server_keys[i].key[j];
generate_key(i);
}
@@ -207,7 +210,9 @@ test_unit(void)
TEST_CHECK(unlink("ntskeys") == 0);
for (i = 0, sum2 = 0; i < MAX_SERVER_KEYS; i++) {
sum2 += server_keys[i].id + server_keys[i].key[0];
sum2 += server_keys[i].id;
for (j = 0; j < sizeof (server_keys[i].key); j++)
sum2 += server_keys[i].key[j];
}
TEST_CHECK(sum == sum2);

View File

@@ -34,74 +34,97 @@ test_unit(void)
unsigned char key[SIV_MAX_KEY_LENGTH], nonce[256], plaintext[256], plaintext2[256];
NTP_PacketInfo info;
NTP_Packet packet;
SIV_Algorithm algo;
SIV_Instance siv;
int i, j, r, packet_length, nonce_length, key_length;
int plaintext_length, plaintext2_length, min_ef_length;
siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
TEST_CHECK(siv);
for (algo = 1; algo < 100; algo++) {
siv = SIV_CreateInstance(algo);
if (!siv) {
TEST_CHECK(algo != AEAD_AES_SIV_CMAC_256);
continue;
}
for (i = 0; i < 10000; i++) {
key_length = SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256);
for (j = 0; j < key_length; j++)
key[j] = random() % 256;
TEST_CHECK(SIV_SetKey(siv, key, key_length));
DEBUG_LOG("algo=%d", (int)algo);
nonce_length = random() % sizeof (nonce) + 1;
for (j = 0; j < nonce_length; j++)
nonce[j] = random() % 256;
for (i = 0; i < 10000; i++) {
key_length = SIV_GetKeyLength(algo);
for (j = 0; j < key_length; j++)
key[j] = random() % 256;
TEST_CHECK(SIV_SetKey(siv, key, key_length));
plaintext_length = random() % (sizeof (plaintext) + 1);
for (j = 0; j < plaintext_length; j++)
plaintext[j] = random() % 256;
assert(sizeof (nonce) >= SIV_GetMinNonceLength(siv));
nonce_length = SIV_GetMinNonceLength(siv) +
random() % (MIN(sizeof (nonce), SIV_GetMaxNonceLength(siv)) -
SIV_GetMinNonceLength(siv) + 1);
for (j = 0; j < nonce_length; j++)
nonce[j] = random() % 256;
packet_length = NTP_HEADER_LENGTH + random() % 100 * 4;
min_ef_length = random() % (sizeof (packet) - packet_length);
plaintext_length = random() % (sizeof (plaintext) + 1);
for (j = 0; j < plaintext_length; j++)
plaintext[j] = random() % 256;
memset(&packet, 0, sizeof (packet));
packet.lvm = NTP_LVM(0, 4, 0);
memset(&info, 0, sizeof (info));
info.version = 4;
info.length = packet_length;
packet_length = NTP_HEADER_LENGTH + random() % 100 * 4;
min_ef_length = random() % (sizeof (packet) - packet_length);
DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d",
packet_length, nonce_length, plaintext_length, min_ef_length);
memset(&packet, 0, sizeof (packet));
packet.lvm = NTP_LVM(0, 4, 0);
memset(&info, 0, sizeof (info));
info.version = 4;
info.length = packet_length;
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
-1, 0);
TEST_CHECK(!r);
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext,
plaintext_length, 0);
TEST_CHECK(!r);
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
plaintext_length, sizeof (packet) - info.length + 1);
TEST_CHECK(!r);
DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d",
packet_length, nonce_length, plaintext_length, min_ef_length);
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
plaintext_length, min_ef_length);
TEST_CHECK(r);
TEST_CHECK(info.length - packet_length >= min_ef_length);
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
-1, 0);
TEST_CHECK(!r);
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext,
plaintext_length, 0);
TEST_CHECK(!r);
if (SIV_GetMinNonceLength(siv) > 1) {
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, SIV_GetMinNonceLength(siv) - 1,
plaintext, plaintext_length, 0);
TEST_CHECK(!r);
TEST_CHECK(info.ext_fields == 0);
}
if (SIV_GetMaxNonceLength(siv) <= sizeof (nonce)) {
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, SIV_GetMaxNonceLength(siv) - 1,
plaintext, plaintext_length, 0);
TEST_CHECK(!r);
TEST_CHECK(info.ext_fields == 0);
}
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
plaintext_length, sizeof (packet) - info.length + 1);
TEST_CHECK(!r);
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
-1, &plaintext2_length);
TEST_CHECK(!r);
r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
plaintext_length, min_ef_length);
TEST_CHECK(r);
TEST_CHECK(info.length - packet_length >= min_ef_length);
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
sizeof (plaintext2), &plaintext2_length);
TEST_CHECK(r);
TEST_CHECK(plaintext_length == plaintext2_length);
TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0);
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
-1, &plaintext2_length);
TEST_CHECK(!r);
j = random() % (packet_length + plaintext_length +
nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4;
((unsigned char *)&packet)[j]++;
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
sizeof (plaintext2), &plaintext2_length);
TEST_CHECK(!r);
((unsigned char *)&packet)[j]--;
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
sizeof (plaintext2), &plaintext2_length);
TEST_CHECK(r);
TEST_CHECK(plaintext_length == plaintext2_length);
TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0);
j = random() % (packet_length + plaintext_length +
nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4;
((unsigned char *)&packet)[j]++;
r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
sizeof (plaintext2), &plaintext2_length);
TEST_CHECK(!r);
((unsigned char *)&packet)[j]--;
}
SIV_DestroyInstance(siv);
}
SIV_DestroyInstance(siv);
}
#else
void

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