Compare commits

...

196 Commits

Author SHA1 Message Date
Miroslav Lichvar
7a03206222 doc: update NEWS 2023-06-21 11:28:54 +02:00
Miroslav Lichvar
b86c50bb9f ntp: refresh IP addresses periodically
Refresh NTP sources specified by hostname periodically (every 2 weeks
by default) to avoid long-running instances using a server which is no
longer intended for service, even if it is still responding correctly
and would not be replaced as unreachable, and help redistributing load
in large pools like pool.ntp.org. Only one source is refreshed at a time
to not interrupt clock updates if there are multiple selectable servers.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The advantages over AES-SIV-CMAC-256 are shorter keys and better
performance.
2022-10-19 15:50:31 +02:00
Miroslav Lichvar
17d2291a84 doc: improve ntsrotate description 2022-10-19 15:33:04 +02:00
Miroslav Lichvar
a6179261a7 doc: fix wrong name of authselectmode directive 2022-10-17 15:24:24 +02:00
Miroslav Lichvar
098e0c43fc test: add float-cast-overflow to 003-sanitizers test 2022-09-20 10:56:28 +02:00
Miroslav Lichvar
7b197953e8 update copyright years 2022-08-29 15:04:33 +02:00
Miroslav Lichvar
9dcace0fc4 doc: improve description of server directive 2022-08-29 15:03:59 +02:00
Miroslav Lichvar
a07ac38331 doc: improve description of system time in tracking report 2022-08-29 12:39:22 +02:00
Miroslav Lichvar
166e43b13e cmdmon: add good responses to ntpdata report 2022-08-18 11:59:40 +02:00
Miroslav Lichvar
b84d6759f9 ntp: initialize remote address in ntpdata report
Don't wait for the first response with setting the address.
2022-08-17 16:14:38 +02:00
Miroslav Lichvar
f323c814af doc: update NEWS 2022-08-11 10:32:58 +02:00
Miroslav Lichvar
19b47dcbc9 doc: mention maxdelayquant in FAQ 2022-08-10 15:32:54 +02:00
Miroslav Lichvar
5edeadcbd9 test: extend 106-refclock test 2022-08-09 16:53:12 +02:00
Miroslav Lichvar
d91ae2094f configure: disable arc4random on Linux
In glibc 2.36 was added the arc4random family of functions. However,
unlike on other supported systems, it is not a user-space PRNG
implementation. It just wraps the getrandom() system call with no
buffering, which causes a performance loss on NTP servers due to
the function being called twice for each response to add randomness
to the RX and TX timestamp below the clock precision.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This issue was reproduced and fix tested on current OpenIndiana.

Fixes: 8feb37df2b ("sys_solaris: use timex driver")
2021-12-07 12:18:56 +01:00
120 changed files with 4517 additions and 1179 deletions

View File

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

51
NEWS
View File

@@ -1,3 +1,49 @@
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
* 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
==================
Enhancements
------------
* Add local option to refclock directive to stabilise system clock
with more stable free-running clock (e.g. TCXO, OCXO)
* Add maxdelayquant option to server/pool/peer directive to replace
maxdelaydevratio filter with long-term quantile-based filtering
* Add selection option to log directive
* Allow external PPS in PHC refclock without configurable pin
* Don't accept first interleaved response to minimise error in delay
* Don't use arc4random on Linux to avoid server performance loss
* Improve filter option to better handle missing NTP samples
* Improve stability with hardware timestamping and PHC refclock
* Update seccomp filter
Bug fixes
---------
* Fix waitsync command to reconnect when not getting response
New in version 4.2
==================
@@ -16,6 +62,11 @@ Bug fixes
---------
* Fix RTC support with 64-bit time_t on 32-bit Linux
* Fix seccomp filter to work correctly with bind*device directives
* Suppress kernel adjustments of system clock (dosynctodr) on illumos
Other changes
-------------
* Switch Solaris support to illumos
New in version 4.1
==================

6
README
View File

@@ -28,7 +28,7 @@ What will chrony run on?
========================
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will
illumos. Closely related systems may work too. Any other system will
likely require a porting exercise.
How do I set it up?
@@ -108,6 +108,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 +122,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,9 +138,11 @@ 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>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.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);

57
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 */
@@ -296,7 +303,8 @@ typedef struct {
uint32_t flags;
int32_t filter_length;
uint32_t cert_set;
uint32_t reserved[2];
Float max_delay_quant;
uint32_t reserved[1];
int32_t EOR;
} REQ_NTP_Source;
@@ -370,6 +378,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
@@ -476,6 +493,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
@@ -518,7 +536,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
@@ -653,17 +672,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;
@@ -733,7 +759,8 @@ typedef struct {
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t reserved[4];
uint32_t total_good_count;
uint32_t reserved[3];
int32_t EOR;
} RPY_NTPData;

313
client.c
View File

@@ -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. */
@@ -283,6 +285,9 @@ open_io(void)
close_io();
}
/* Start from the first address if called again */
address_index = 0;
return 0;
}
@@ -866,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)
{
@@ -943,12 +959,11 @@ process_cmd_add_source(CMD_Request *msg, char *line)
(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));
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 =
UTI_FloatHostToNetwork(data.params.max_delay_quant);
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
result = 1;
@@ -1008,6 +1023,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"
@@ -1122,8 +1138,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
};
@@ -1151,7 +1167,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);
}
}
@@ -1457,24 +1473,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);
}
}
}
@@ -1605,8 +1621,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;
@@ -1704,9 +1721,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);
@@ -1773,8 +1790,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);
@@ -1791,14 +1808,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--)
@@ -1958,7 +1979,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);
@@ -1995,9 +2016,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:
@@ -2046,7 +2066,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),
@@ -2107,9 +2127,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),
@@ -2157,7 +2177,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),
@@ -2245,10 +2265,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),
@@ -2341,18 +2361,18 @@ process_cmd_ntpdata(char *line)
"RX timestamping : %N\n"
"Total TX : %U\n"
"Total RX : %U\n"
"Total valid RX : %U\n",
UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
"Total valid RX : %U\n"
"Total good RX : %U\n",
UTI_IPToString(&remote_addr), UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port),
UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
UTI_IPToString(&local_addr), UTI_IPToRefid(&local_addr),
reply.data.ntp_data.leap, reply.data.ntp_data.version,
reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
(unsigned long)ntohl(reply.data.ntp_data.ref_id),
reply.data.ntp_data.stratum <= 1 ?
ntohl(reply.data.ntp_data.ref_id), reply.data.ntp_data.stratum <= 1 ?
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
&ref_time,
UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
@@ -2366,9 +2386,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),
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);
}
@@ -2396,12 +2417,12 @@ process_cmd_selectdata(char *line)
n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) {
printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n");
printf( "| x - falseticker, P - not preferred, U - waits for update,\n");
printf( "| S - stale, O - orphan, + - combined, * - best.\n");
printf( "| Effective options ------. (N - noselect, P - prefer\n");
printf( "| Configured options -. \\ T - trust, R - require)\n");
printf( " . State: N - noselect, s - unsynchronised, M - missing samples,\n");
printf( " / d/D - large distance, ~ - jittery, w/W - waits for others,\n");
printf( "| S - stale, O - orphan, T - not trusted, P - not preferred,\n");
printf( "| U - waits for update,, x - falseticker, + - combined, * - best.\n");
printf( "| Effective options ---------. (N - noselect, P - prefer\n");
printf( "| Configured options ----. \\ T - trust, R - require)\n");
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
printf( "| | | | |\n");
}
@@ -2440,7 +2461,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),
@@ -2460,31 +2481,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;
@@ -2566,7 +2599,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);
@@ -2641,16 +2674,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);
}
@@ -2681,7 +2713,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");
@@ -2801,11 +2833,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;
@@ -2884,6 +2916,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)
{
@@ -2919,7 +3000,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 */)) &&
@@ -3186,6 +3267,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);
@@ -3235,44 +3318,50 @@ process_line(char *line)
if (do_normal_submit) {
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
}
if (end_dot) {
printf(".\n");
}
fflush(stderr);
fflush(stdout);
if (fflush(stdout) != 0 || ferror(stdout) != 0) {
LOG(LOGS_ERR, "Could not write to stdout");
/* Return error for commands that print data */
if (!do_normal_submit)
return 0;
}
return ret;
}
/* ================================================== */
#define MAX_LINE_LENGTH 2048
static int
process_args(int argc, char **argv, int multi)
{
int total_length, i, ret = 0;
char *line;
char line[MAX_LINE_LENGTH];
int i, l, ret = 0;
total_length = 0;
for(i=0; i<argc; i++) {
total_length += strlen(argv[i]) + 1;
for (i = l = 0; i < argc; i++) {
l += snprintf(line + l, sizeof (line) - l, "%s ", argv[i]);
if (l >= sizeof (line)) {
LOG(LOGS_ERR, "Command too long");
return 0;
}
line = (char *) Malloc((2 + total_length) * sizeof(char));
for (i = 0; i < argc; i++) {
line[0] = '\0';
if (multi) {
strcat(line, argv[i]);
} else {
for (; i < argc; i++) {
strcat(line, argv[i]);
if (i + 1 < argc)
strcat(line, " ");
}
}
if (!multi && i + 1 < argc)
continue;
ret = process_line(line);
if (!ret || quit)
break;
}
Free(line);
l = 0;
}
return ret;
}
@@ -3291,7 +3380,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2022 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",
@@ -3310,6 +3399,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
@@ -3354,7 +3444,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':
@@ -3372,6 +3462,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

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -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)
{
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]++;
}
/* ================================================== */
@@ -751,7 +762,7 @@ static void
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
{
ntp_ts->hi = htonl(ts >> 32);
ntp_ts->lo = htonl(ts & ((1ULL << 32) - 1));
ntp_ts->lo = htonl(ts);
}
/* ================================================== */
@@ -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. */

101
cmdmon.c
View File

@@ -144,6 +144,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
};
/* ================================================== */
@@ -321,7 +322,8 @@ transmit_reply(int sock_fd, int request_length, SCK_Message *message)
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message->local_addr.ip.family == IPADDR_INET4 && (sock_fd != sock_fd4 || bound_sock_fd4))
if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
(sock_fd != sock_fd4 || bound_sock_fd4))
message->local_addr.ip.family = IPADDR_UNSPEC;
#endif
@@ -702,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)
{
@@ -756,6 +769,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
params.max_delay_dev_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
params.max_delay_quant =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
@@ -770,11 +785,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
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.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) {
@@ -1166,18 +1177,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));
}
/* ================================================== */
@@ -1221,6 +1250,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
@@ -1324,7 +1354,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) |
@@ -1351,14 +1381,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 */
@@ -1511,6 +1559,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 */
@@ -1753,11 +1803,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);
}
@@ -1791,6 +1847,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

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014, 2016
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -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;
@@ -72,6 +72,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.max_delay_quant = 0.0;
src->params.min_delay = 0.0;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
src->params.offset = 0.0;
@@ -100,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;
@@ -140,6 +133,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelayquant")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
@@ -183,6 +179,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;
}
@@ -193,6 +191,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)
{
@@ -392,3 +407,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 */

90
conf.c
View File

@@ -115,6 +115,7 @@ static int cmd_port = DEFAULT_CANDM_PORT;
static int raw_measurements = 0;
static int do_log_measurements = 0;
static int do_log_selection = 0;
static int do_log_statistics = 0;
static int do_log_tracking = 0;
static int do_log_rtc = 0;
@@ -251,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;
@@ -273,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;
@@ -601,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")) {
@@ -696,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")) {
@@ -861,16 +872,16 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int max_lock_age, pps_forced, stratum, tai;
int local, max_lock_age, pps_forced, 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;
dpoll = 0;
filter_length = 64;
local = 0;
pps_forced = 0;
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
@@ -910,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;
@@ -929,6 +938,9 @@ parse_refclock(char *line)
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
break;
}
} else if (!strcasecmp(cmd, "local")) {
n = 0;
local = 1;
} else if (!strcasecmp(cmd, "rate")) {
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
break;
@@ -966,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;
@@ -995,6 +998,7 @@ parse_refclock(char *line)
refclock->driver_poll = dpoll;
refclock->poll = poll;
refclock->filter_length = filter_length;
refclock->local = local;
refclock->pps_forced = pps_forced;
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
@@ -1027,6 +1031,8 @@ parse_log(char *line)
raw_measurements = 1;
} else if (!strcmp(log_name, "measurements")) {
do_log_measurements = 1;
} else if (!strcmp(log_name, "selection")) {
do_log_selection = 1;
} else if (!strcmp(log_name, "statistics")) {
do_log_statistics = 1;
} else if (!strcmp(log_name, "tracking")) {
@@ -1429,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();
@@ -1460,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;
@@ -1495,6 +1505,9 @@ parse_hwtimestamp(char *line)
if (*p)
command_parse_error();
if (!maxpoll_set)
iface->maxpoll = iface->minpoll + 1;
}
/* ================================================== */
@@ -1696,6 +1709,8 @@ 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; ) {
@@ -1731,6 +1746,8 @@ reload_source_dirs(void)
}
}
LOG_UnsetContext(LOGC_SourceFile);
for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name);
Free(prev_sources);
@@ -1774,6 +1791,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)
{
@@ -1924,6 +1954,14 @@ CNF_GetLogMeasurements(int *raw)
/* ================================================== */
int
CNF_GetLogSelection(void)
{
return do_log_selection;
}
/* ================================================== */
int
CNF_GetLogStatistics(void)
{
@@ -2484,6 +2522,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */
double
CNF_GetHwTsTimeout(void)
{
return hwts_timeout;
}
/* ================================================== */
int
CNF_GetPtpPort(void)
{
@@ -2492,6 +2538,14 @@ CNF_GetPtpPort(void)
/* ================================================== */
int
CNF_GetRefresh(void)
{
return refresh;
}
/* ================================================== */
char *
CNF_GetNtsDumpDir(void)
{

7
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);
@@ -58,6 +60,7 @@ extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogSelection(void);
extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
@@ -141,6 +144,7 @@ typedef enum {
typedef struct {
char *name;
int minpoll;
int maxpoll;
int min_samples;
int max_samples;
int nocrossts;
@@ -151,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);

59
configure vendored
View File

@@ -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,7 +245,9 @@ 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
feat_timestamping=1
try_timestamping=0
@@ -344,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
;;
@@ -421,6 +427,7 @@ case $OPERATINGSYSTEM in
try_setsched=1
try_lockmem=1
try_phc=1
try_arc4random=0
add_def LINUX
echo "Configuring for " $SYSTEM
;;
@@ -467,7 +474,7 @@ case $OPERATINGSYSTEM in
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
LIBS="$LIBS -lsocket -lnsl -lresolv"
LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
try_setsched=1
try_lockmem=1
add_def SOLARIS
@@ -479,7 +486,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
fi
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
;;
* )
echo "error: $SYSTEM is not supported (yet?)"
@@ -671,12 +678,12 @@ fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);'
'clock_gettime(CLOCK_REALTIME, (void *)1);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, NULL);'
'clock_gettime(CLOCK_REALTIME, (void *)1);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
@@ -702,11 +709,14 @@ then
use_pthread=1
fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
if [ $try_arc4random = "1" ] && \
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
'arc4random_buf((void *)1, 1);'
then
add_def HAVE_ARC4RANDOM
else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then
'return getrandom((void *)1, 1, 0);'; then
add_def HAVE_GETRANDOM
fi
fi
@@ -900,7 +910,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
add_def FEAT_SECHASH
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
'cmac128_update(NULL, NULL, NULL, 0, NULL);'
'cmac128_update((void *)1, (void *)2, (void *)3, 1, (void *)4);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
@@ -925,7 +935,7 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
'hash_memory_multi(find_hash("md5"), (void *)1, (void *)2, (void *)3, 1, (void *)4, 1);'
then
HASH_OBJ="hash_tomcrypt.o"
HASH_LINK="-ltomcrypt"
@@ -939,7 +949,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash(NULL, NULL, 0);'
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
@@ -947,7 +957,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init(NULL, GNUTLS_MAC_AES_CMAC_128, NULL, 0);'
'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"
@@ -970,27 +980,40 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
fi
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
return gnutls_init((void *)1, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
then
if test_code 'SIV in nettle' \
if test_code 'AES-SIV-CMAC in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);'
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
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(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
then
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
fi
else
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);'
'aes128_set_encrypt_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV

View File

@@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2021
// Copyright (C) Miroslav Lichvar 2009-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
@@ -63,16 +63,26 @@ source. The client-server relationship is strictly hierarchical: a client might
synchronise its system time to that of the server, but the server's system time
will never be influenced by that of a client.
+
The server can be specified by its hostname or IP address. If the hostname cannot
be resolved on start, *chronyd* will try it again in increasing intervals, and
also when the <<chronyc.adoc#online,*online*>> command is issued in *chronyc*.
+
The DNS record can change over time. The used address will be replaced with a
newly resolved address when the server becomes unreachable (i.e. no valid
response to last 8 requests), unsynchronised, a falseticker (i.e. does not
agree with a majority of other sources), or the root distance is too large (the
limit can be configured by the <<maxdistance,*maxdistance*>> directive). The
automatic replacement happens at most once per 30 minutes.
+
This directive can be used multiple times to specify multiple servers.
+
The directive is immediately followed by either the name of the
server, or its IP address. It supports the following options:
The directive supports the following options:
+
*minpoll* _poll_:::
This option specifies the minimum interval between requests sent to the server
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
polling interval should not drop below 32 seconds. The default is 6 (64
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
months). Note that intervals shorter than 6 (64 seconds) should generally not
be used with public servers on the Internet, because it might be considered
abuse. A sub-second interval will be enabled only when the server is reachable
@@ -82,7 +92,7 @@ should be in a local network.
This option specifies the maximum interval between requests sent to the server
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
months).
*iburst*:::
With this option, *chronyd* will start with a burst of 4-8 requests in order to
@@ -138,18 +148,33 @@ behind a lot of packets related to a download of some sort).
If the user knows that round trip delays above a certain level should cause the
measurement to be ignored, this level can be defined with the *maxdelay*
option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is
3 seconds and the maximum value is 1000 seconds.
round-trip delay greater than 0.3 seconds should be ignored. The default value
is 3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_:::
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
of the minimum round-trip delay amongst the previous measurements that it has
buffered. If a measurement has a round trip delay that is greater than the
specified ratio times the minimum delay, it will be rejected.
buffered. If a measurement has a round-trip delay that is greater than the
specified ratio times the minimum delay, it will be rejected. By default, this
test is disabled.
*maxdelaydevratio* _ratio_:::
If a measurement has a ratio of the increase in the round-trip delay from the
minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0.
*maxdelayquant* _p_:::
This option disables the *maxdelaydevratio* test and specifies the maximum
acceptable delay as a quantile of the round-trip delay instead of a function of
the minimum delay amongst the buffered measurements. If a measurement has a
round-trip delay that is greater than a long-term estimate of the _p_-quantile,
it will be rejected.
+
The specified _p_ value should be between 0.05 and 0.95. For example,
*maxdelayquant 0.2* would indicate that only measurements with the lowest 20
percent of round-trip delays should be accepted. Note that it can take many
measurements for the estimated quantile to reach the expected value. This
option is intended for synchronisation in mostly static local networks with
very short polling intervals and possibly combined with the *filter* option.
By default, this test is disabled in favour of the *maxdelaydevratio* test.
*mindelay* _delay_:::
This option specifies a fixed minimum round-trip delay to be used instead of
the minimum amongst the previous measurements. This can be useful in networks
@@ -176,11 +201,12 @@ Set the minimum number of samples kept for this source. This overrides the
*maxsamples* _samples_:::
Set the maximum number of samples kept for this source. This overrides the
<<maxsamples,*maxsamples*>> directive.
*filter* _samples_:::
*filter* _polls_:::
This option enables a median filter to reduce noise in NTP measurements. The
filter will reduce the specified number of samples to a single sample. It is
intended to be used with very short polling intervals in local networks where
it is acceptable to generate a lot of NTP traffic.
filter will process samples collected in the specified number of polls
into a single sample. It is intended to be used with very short polling
intervals in local networks where it is acceptable to generate a lot of NTP
traffic.
*offline*:::
If the server will not be reachable when *chronyd* is started, the *offline*
option can be specified. *chronyd* will not try to poll the server until it is
@@ -325,12 +351,6 @@ This option sets the desired number of sources to be used from the pool.
sources responding to requests. The default value is 4 and the maximum value is
16.
+
{blank}::
When an NTP source is unreachable,
marked as a falseticker, or has a distance larger than the limit set by the
<<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
source with a newly resolved address of the name.
+
An example of the *pool* directive is
+
----
@@ -451,16 +471,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
@@ -468,6 +513,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:
+
----
@@ -475,23 +530,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
@@ -511,8 +549,10 @@ Note that some PTP clocks cannot be configured to timestamp only assert or
clear events, and it is necessary to use the *width* option to filter wrong
PPS samples.
*pin*=_index_::::
This option specifies the index of the pin to which is connected the PPS
signal. The default value is 0.
This option specifies the index of the pin which should be enabled for the
PPS timestamping. If the PHC does not have configurable pins (i.e. the channel
function is fixed), the index needs to be set to -1 to disable the pin
configuration. The default value is 0.
*channel*=_index_::::
This option specifies the index of the channel for the PPS mode. The default
value is 0.
@@ -600,7 +640,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*:::
@@ -624,6 +666,13 @@ and that *chronyd* should correct its offset by the current TAI-UTC offset. The
<<leapsectz,*leapsectz*>> directive must be used with this option and the
database must be kept up to date in order for this correction to work as
expected. This option does not make sense with PPS refclocks.
*local*:::
This option specifies that the reference clock is an unsynchronised clock which
is more stable than the system clock (e.g. TCXO, OCXO, or atomic clock) and
it should be used as a local standard to stabilise the system clock. The
refclock will bypass the source selection. There should be at most one refclock
specified with this option and it should have the shortest polling interval
among all configured sources.
*minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
@@ -778,6 +827,10 @@ 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
@@ -831,6 +884,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_::
@@ -892,7 +958,7 @@ 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
refclock SOCK /var/run/chrony.ttyS0.sock
----
+
is equivalent to the following configuration using the *ignore* mode:
@@ -902,7 +968,7 @@ 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
refclock /var/run/chrony.ttyS0.sock require trust
----
[[combinelimit]]*combinelimit* _limit_::
@@ -1099,7 +1165,11 @@ clock will be off for a longer time. On Linux with the default
*ignore*:::
No correction is applied to the clock for the leap second. The clock will be
corrected later in normal operation when new measurements are made and the
estimated offset includes the one second error.
estimated offset includes the one second error. This option is particularly
useful when multiple *chronyd* instances are running on the system, one
controlling the system clock and others started with the *-x* option, which
should rely on the first instance to correct the system clock and ignore it for
the correction of their own NTP clock running on top of the system clock.
{blank}::
+
When serving time to NTP clients that cannot be configured to correct their
@@ -1206,12 +1276,16 @@ This would step the system clock if the adjustment is larger than 0.1 seconds, b
only in the first three clock updates.
[[maxchange]]*maxchange* _offset_ _start_ _ignore_::
This directive sets the maximum allowed offset corrected on a clock update. The
check is performed only after the specified number of updates to allow a large
initial adjustment of the system clock. When an offset larger than the
specified maximum occurs, it will be ignored for the specified number of times
and then *chronyd* will give up and exit (a negative value can be used to never
exit). In both cases a message is sent to syslog.
This directive sets the maximum offset to be accepted on a clock update. The
offset is measured relative to the current estimate of the true time, which is
different from the system time if a previous slew did not finish.
+
The check is enabled after the specified number of clock updates to allow a
large initial offset to be corrected on start. Offsets larger than the
specified maximum will be ignored for the specified number of times. Another
large offset will cause *chronyd* to give up and exit. A negative value
can be used to disable the limit to ignore all large offsets. A syslog message
will be generated when an offset is ignored or it causes the exit.
+
An example of the use of this directive is:
+
@@ -1238,7 +1312,7 @@ This directive specifies the maximum assumed drift (frequency error) of the
system clock. It limits the frequency adjustment that *chronyd* is allowed to
use to correct the measured drift. It is an additional limit to the maximum
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on illumos).
+
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
limited by the system driver rather than this directive.
@@ -1263,10 +1337,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
@@ -1277,7 +1350,7 @@ all supported systems with the exception of macOS 12 or earlier).
+
For each system there is a maximum frequency offset of the clock that can be set
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
is 5000 ppm, and on illumos it is 32500 ppm. Also, due to a kernel limitation,
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
ppm and 5000 ppm will effectively set it to 500 ppm.
+
@@ -1696,7 +1769,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.
+
@@ -2051,9 +2127,11 @@ from the example line above):
. Stratum of remote computer. [2]
. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111]
. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
. Tests for maximum delay, maximum delay ratio and maximum delay dev ratio,
against defined parameters, and a test for synchronisation loop (1=pass,
0=fail) [1111]
. Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
*maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
0=fail). The first test from these four also checks the server precision,
response time, and whether an interleaved response is acceptable for
synchronisation. [1111]
. Local poll [10]
. Remote poll [10]
. '`Score`' (an internal score within each polling level used to decide when to
@@ -2127,6 +2205,78 @@ from the example line above):
the source is more variable than the delay of packets sent from the source
back. [0.00, i.e. no correction for asymmetry]
+
*selection*:::
This option logs information about selection of sources for synchronisation to
a file called _selection.log_. Note that the rate of entries written to this
file grows quadratically with the number of specified sources (each measurement
triggers the selection for all sources). An example line (which actually
appears as a single line in the file) from the log file is shown below.
+
----
2022-05-01 02:01:20 203.0.113.15 * ----- 377 1.00 \
4.228e+01 -1.575e-04 1.239e-04
----
+
The columns are as follows (the quantities in square brackets are the values
from the example line above):
+
. Date [2022-05-01]
. Hour:Minute:Second. Note that the date-time pair is expressed in
UTC, not the local time zone. [02:01:20]
. IP address or reference ID of the source. [203.0.113.15]
. State of the source indicated with one of the following symbols. [*]
{blank}::::
Not considered selectable for synchronisation:
* _N_ - has the *noselect* option.
* _s_ - is not synchronised.
* _M_ - does not have enough measurements.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
<<maxjitter,*maxjitter*>> directive).
* _w_ - waits for other sources to get out of the _M_ state.
* _S_ - has older measurements than other sources.
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
the <<local,*local*>> directive).
* _T_ - does not fully agree with sources that have the *trust* option.
* _x_ - does not agree with other sources (falseticker).
{blank}::::
Considered selectable for synchronisation, but not currently used:
* _W_ - waits for other sources to be selectable (required by the
<<minsources,*minsources*>> directive, or the *require* option of
another source).
* _P_ - another selectable source is preferred due to the *prefer* option.
* _U_ - waits for a new measurement (after selecting a different best source).
* _D_ - has, or recently had, a root distance which is too large to be combined
with other sources (configured by the <<combinelimit,*combinelimit*>>
directive).
{blank}::::
Used for synchronisation of the local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
. 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
transmissions. [377]
. Current score against the source in the _*_ state. The scoring system avoids
frequent reselection when multiple sources have a similar root distance. A
value larger than 1 indicates this source was better than the _*_ source in
recent selections. If the score reaches 10, the best source will be reselected
and the scores will be reset to 1. [1.00]
. Interval since the last measurement of the source in seconds. [4.228e+01]
. Lower endpoint of the interval which was expected to contain the true offset
of the local clock determined by the root distance of the source. [-1.575e-04]
. Upper endpoint of the interval which was expected to contain the true offset
of the local clock determined by the root distance of the source. [1.239e-04]
+
*tracking*:::
This option logs changes to the estimate of the system's gain or loss rate, and
any slews made, to a file called _tracking.log_. An example line (which
@@ -2381,19 +2531,29 @@ be enabled by the *xleave* option in the <<server,*server*>> or the
+
This directive is supported on Linux 3.19 and newer. The NIC must support HW
timestamping, which can be verified with the *ethtool -T* command. The list of
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
_SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive
filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for
timestamping of received NTP packets. Timestamping of packets received on bridged
and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is
running, no other process (e.g. a PTP daemon) should be working with the NIC
clock.
capabilities should include _hardware-raw-clock_, _hardware-transmit_, and
_hardware-receive_. The receive filter _all_, or _ntp_, is necessary for
timestamping of received NTP packets. Timestamping of packets received on
bridged and bonded interfaces is supported on Linux 4.13 and newer. If HW
timestamping does not work for received packets, *chronyd* will use kernel
receive timestamps instead. Transmit-only HW timestamping can still be useful
to improve stability of the synchronisation.
+
*chronyd* does not synchronise the NIC clock. It assumes the clock is running
free. Multiple instances of *chronyd* can use the same interface with enabled
HW timestamping. Applications which need HW timestamping with a synchronised
clock (e.g. a PTP daemon) should use a virtual clock running on top of the
physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
feature is available on Linux 5.14 and newer.
+
If the kernel supports software timestamping, it will be enabled for all
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
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
@@ -2403,10 +2563,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.
@@ -2445,7 +2610,8 @@ is selected by default. Some NICs can timestamp PTP packets only. By default,
they will be configured with the _none_ filter and expected to provide hardware
timestamps for transmitted packets only. Timestamping of PTP packets is useful
with NTP-over-PTP enabled by the <<chrony.conf.adoc#ptpport,*ptpport*>>
directive. Forcing timestamping of all packets with the _all_ filter could be
directive, or when another application is receiving PTP packets on the
interface. Forcing timestamping of all packets with the _all_ filter could be
useful if the NIC supported both the _all_ and _ntp_ filters, and it should
timestamp both NTP and PTP packets, or NTP packets on a different UDP port.
{blank}::
@@ -2458,6 +2624,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
@@ -2491,7 +2678,7 @@ The type is a name of a cryptographic hash function or cipher which is used to
generate and verify the MAC. The default type is *MD5*, which is always
supported.
If *chronyd* was built with enabled support for hashing using a crypto library
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
(Nettle, GnuTLS, NSS, or LibTomCrypt), the following functions are available: *MD5*,
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
*chronyd* using, some of the following hash functions and ciphers may
also be available:
@@ -2524,7 +2711,7 @@ file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
[[lock_all]]*lock_all*::
The *lock_all* directive will lock the *chronyd* process into RAM so that it
will never be paged out. This can result in lower and more consistent latency.
The directive is supported on Linux, FreeBSD, NetBSD, and Solaris.
The directive is supported on Linux, FreeBSD, NetBSD, and illumos.
[[pidfile]]*pidfile* _file_::
Unless *chronyd* is started with the *-Q* option, it writes its process ID
@@ -2564,7 +2751,7 @@ ptpport 319
----
[[sched_priority]]*sched_priority* _priority_::
On Linux, FreeBSD, NetBSD, and Solaris, the *sched_priority* directive will
On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
select the SCHED_FIFO real-time scheduler at the specified priority (which must
be between 0 and 100). On macOS, this option must have either a value of 0 (the
default) to disable the thread time constraint policy or 1 for the policy to be
@@ -2590,7 +2777,7 @@ The *user* directive sets the name of the system user to which *chronyd* will
switch after start in order to drop root privileges.
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
On macOS, FreeBSD, NetBSD and illumos *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
+

View File

@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2020
// Copyright (C) Miroslav Lichvar 2009-2017, 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
@@ -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.
@@ -99,12 +103,15 @@ With this option multiple commands can be specified. Each argument will be
interpreted as a whole command.
*-h* _host_::
This option allows the user to specify which host (or comma-separated list of
addresses) running the *chronyd* program is to be contacted. This allows for
remote monitoring, without having to connect over SSH to the other host first.
This option specifies the host to be contacted by *chronyc*. It can be
specified with a hostname, IP address, or path to the local Unix domain socket.
Multiple values can be specified as a comma-separated list to provide a
fallback.
+
The default is to contact *chronyd* running on the same host where
*chronyc* is being run.
The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
where *chronyc* is being run. First, it tries to connect to the Unix domain
socket and if that fails (e.g. due to running under a non-root user), it
will try to connect to 127.0.0.1 and then ::1.
*-p* _port_::
This option allows the user to specify the UDP port number which the target
@@ -177,15 +184,19 @@ stratum-2 and is synchronised from a stratum-1).
This is the time (UTC) at which the last measurement from the reference
source was processed.
*System time*:::
In normal operation, *chronyd* by default never steps the system clock, because
any jump in the time can have adverse consequences for certain application
programs. Instead, any error in the system clock is corrected by slightly
speeding up or slowing down the system clock until the error has been removed,
and then returning to the system clock's normal speed. A consequence of this is
that there will be a period when the system clock (as read by other programs)
will be different from *chronyd*'s estimate of the current true time (which it
reports to NTP clients when it is operating as a server). The value reported
on this line is the difference due to this effect.
This is the current offset between the NTP clock and system clock. The NTP
clock is a software (virtual) clock maintained by *chronyd*, which is
synchronised to the configured time sources and provides time to NTP clients.
The system clock is synchronised to the NTP clock. To avoid steps in the
system time, which might have adverse consequences for certain applications,
the system clock is normally corrected only by speeding up or slowing down (up
to the rate configured by the <<chrony.conf.adoc#maxslewrate,*maxslewrate*>>
directive). If the offset is too large, this correction will take a very long
time. A step can be forced by the <<makestep,*makestep*>> command, or the
<<chrony.conf.adoc#makestep,*makestep*>> directive in the configuration file.
+
Note that all other offsets reported by *chronyc* and most offsets in the log
files are relative to the NTP clock, not the system clock.
*Last offset*:::
This is the estimated local offset on the last clock update. A positive value
indicates the local time (as previously estimated true time) was ahead of the
@@ -448,6 +459,7 @@ states are reported.
The following states indicate the source is not considered selectable for
synchronisation:
* _N_ - has the *noselect* option.
* _s_ - is not synchronised.
* _M_ - does not have enough measurements.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
@@ -492,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.
@@ -514,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
@@ -602,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*:::
@@ -658,6 +688,7 @@ RX timestamping : Kernel
Total TX : 24
Total RX : 24
Total valid RX : 24
Total good RX : 22
----
+
The fields are explained as follows:
@@ -695,7 +726,8 @@ packets sent to the source is more variable than the delay of packets sent
from the source back.
*NTP tests*:::
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
delay, delay ratio, delay dev ratio, and synchronisation loop.
delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
loop.
*Interleaved*:::
This shows if the response was in the interleaved mode.
*Authenticated*:::
@@ -710,7 +742,10 @@ The number of packets sent to the source.
*Total RX*:::
The number of all packets received from the source.
*Total valid RX*:::
The number of valid packets received from the source.
The number of packets which passed the first two groups of NTP tests.
*Total good RX*:::
The number of packets which passed all three groups of NTP tests, i.e. the NTP
measurement was accepted.
[[add_peer]]*add peer* _name_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
@@ -935,12 +970,17 @@ 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
@@ -1123,6 +1163,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:
@@ -1157,10 +1203,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
@@ -1199,8 +1259,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

View File

@@ -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
@@ -100,7 +102,7 @@ directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
Solaris, and macOS 10.13 or later).
illumos, and macOS 10.13 or later).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -141,7 +143,7 @@ after start in order to drop root privileges. It overrides the
_@DEFAULT_USER@_.
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
@@ -181,7 +183,7 @@ limited.
The filters cannot be enabled with the *mailonchange* directive.
*-P* _priority_::
On Linux, FreeBSD, NetBSD, and Solaris, this option will select the SCHED_FIFO
On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
real-time scheduler at the specified priority (which must be between 0 and
100). On macOS, this option must have either a value of 0 to disable the thread
time constraint policy or 1 for the policy to be enabled. Other systems do not
@@ -189,7 +191,7 @@ support this option. The default value is 0.
*-m*::
This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux, FreeBSD, NetBSD, and Solaris.
This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
*-x*::
This option disables the control of the system clock. *chronyd* will not try to

View File

@@ -1,7 +1,7 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2021
// Copyright (C) Miroslav Lichvar 2014-2016, 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
@@ -232,7 +232,7 @@ 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
@@ -345,6 +345,15 @@ server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
hwtimestamp eth0 minpoll -6
----
Since `chrony` version 4.3, the minimum `minpoll` is -7 and a filter using a
long-term estimate of a delay quantile can be enabled by the `maxdelayquant`
option to replace the default `maxdelaydevratio` filter, which is sensitive to
outliers corrupting the minimum delay. For example:
----
server ntp.local minpoll -7 maxpoll -7 filter 31 maxdelayquant 0.3 xleave
----
As an experimental feature added in version 4.2, `chronyd` supports an NTPv4
extension field containing an additional timestamp to enable frequency transfer
and significantly improve stability of synchronisation. It can be enabled by
@@ -475,6 +484,72 @@ identically configured leap-smearing servers. Note that some clients can get
leap seconds from other sources (e.g. with the `leapsectz` directive in
`chrony`) and they will not work correctly with a leap smearing server.
=== How should `chronyd` be configuration with `gpsd`?
A GPS or other GNSS receiver can be used as a reference clock with `gpsd`. It
can work as one or two separate time sources for each connected receiver. The
first time source is based on timestamping of messages sent by the receiver.
Typically, it is accurate to milliseconds. The other source is much more
accurate. It is timestamping a pulse-per-second (PPS) signal, usually connected
to a serial port (e.g. DCD pin) or GPIO pin.
If the PPS signal is connected to the serial port which is receiving messages
from the GPS/GNSS receiver, `gpsd` should detect and use it automatically. If
it is connected to a GPIO pin, or another serial port, the PPS device needs to
be specified on the command line as an additional data source. On Linux, the
`ldattach` utility can be used to create a PPS device for a serial device.
The 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).
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 PPS /dev/pps0 lock NMEA refid GPS
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid NMEA noselect
----
They both have some advantages:
* `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 `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
----
=== Does `chrony` support PTP?
No, the Precision Time Protocol (PTP) is not supported as a protocol for
@@ -498,6 +573,27 @@ transport for NTP messages (NTP over PTP) to enable hardware timestamping on
hardware which can timestamp PTP packets only. It can be enabled by the
`ptpport` directive.
=== Why are client log records dropped before reaching `clientloglimit`?
The number of dropped client log records reported by the `serverstats` command
can be increasing before the number of clients reported by the `clients` command
reaches the maximum value corresponding to the memory limit set by the
`clientloglimit` directive.
This is due to the design of the data structure keeping the client records. It
is a hash table which can store only up to 16 colliding addresses per slot. If
a slot has more collisions and the table already has the maximum size, the
oldest record will be dropped and replaced by the new client.
Note that the size of the table is always a power of two and it can only grow.
The limit set by the `clientloglimit` directive takes into account that two
copies of the table exist when it is being resized. This means the actual
memory usage reported by `top` and other utilities can be significantly smaller
than the limit even when the maximum number of records is used.
The absolute maximum number of client records kept at the same time is
16777216.
=== What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the
@@ -517,8 +613,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
@@ -624,6 +721,18 @@ was not shut down for too long and the server's certificate was not renewed too
close to its expiration, it should be sufficient for the time checks to
succeed.
If you run your own server, you can use a self-signed certificate covering
all dates where the client can start (e.g. years 1970-2100). The certificate
needs to be installed on the client and specified with the `ntstrustedcerts`
directive. The server can have multiple names and certificates. To avoid
trusting a certificate for too long, a new certificate can be added to the
server periodically (e.g. once per year) and the client can have the server
name and trusted certificate updated automatically (e.g. using a package
repository, or a cron script downloading the files directly from the server
over HTTPS). A client that was shut down for years will still be able to
synchronise its clock and perform the update as long as the server keeps
the old certificate.
As a last resort, you can disable the time checks by the `nocerttimecheck`
directive. This has some important security implications. To reduce the
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
@@ -692,12 +801,14 @@ frequently, you can effectively disable the test by setting the
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
recovery by increasing the clock error rate with the `maxclockerror` directive.
[[using-pps-refclock]]
=== Using a PPS reference clock?
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
determine which second of UTC corresponds to each pulse. If it is another
reference clock specified with the `lock` option in the `refclock` directive,
the offset between the two reference clocks must be smaller than 0.2 seconds in
the offset between the two reference clocks must be smaller than 0.4 seconds
(0.2 seconds with `chrony` versions before 4.1) in
order for the PPS reference clock to work. With NMEA reference clocks it is
common to have a larger offset. It needs to be corrected with the `offset`
option.
@@ -716,7 +827,7 @@ foo.example.net 7 3 200 -2.991 16.141 -107us 492us
the offset of the NMEA source would need to be increased by about 0.504
seconds. It does not have to be very accurate. As long as the offset of the
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
NMEA reference clock stays below the limit, the PPS reference clock should be
able to determine the seconds corresponding to the pulses and allow the samples
to be used for synchronisation.
@@ -772,6 +883,10 @@ in parentheses) on the `Reference ID` line.
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
(`chronyc` side).
Note that this protocol is not compatible with the mode 6 or mode 7 protocol
supported by `ntpd`, i.e. the `ntpq` or `ntpdc` utility cannot be used to
monitor `chronyd`, and `chronyc` cannot be used to monitor `ntpd`.
== Real-time clock issues
=== What is the real-time clock (RTC)?
@@ -898,6 +1013,34 @@ timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
synchronisation of its own clock, which will cause the other computer to
measure a significant offset.
== Operation
=== What clocks does `chronyd` use?
There are several different clocks used by `chronyd`:
* *System clock:* software clock maintained by the kernel. It is the main clock
used by applications running on the computer. It is synchronised by `chronyd`
to its NTP clock, unless started with the *-x* option.
* *NTP clock:* software clock (virtual) based on the system clock and internal
to `chronyd`. It keeps the best estimate of the true time according to the
configured time sources, which is served to NTP clients unless time smoothing
is enabled by the *smoothtime* directive. The *System time* value in the
`tracking` report is the current offset between the system and NTP clock.
* *Real-time clock (RTC):* hardware clock keeping time even when the
computer is turned off. It is used by the kernel to initialise the system
clock on boot and also by `chronyd` to compensate for its measured drift if
configured with the `rtcfile` directive and started with the `-s` option.
The clock can be kept accurate only by stepping enabled by the `rtcsync` or
`rtcautotrim` directive.
* *Reference clock:* hardware clock used as a time source. It is specified by
the `refclock` directive.
* *NIC clock (also known as PTP hardware clock):* hardware clock timestamping
packets received and transmitted by a network device specified by the
*hwtimestamp* directive. The clock is expected to be running free. It is not
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
order to convert the hardware timestamps.
== Operating systems
=== Does `chrony` support Windows?

View File

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

View File

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

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

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

View File

@@ -7,8 +7,20 @@ export LC_ALL=C
chronyc=/usr/bin/chronyc
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# For NetworkManager consider only selected events
if [ $# -ge 2 ]; then
case "$2" in
up|down|connectivity-change)
;;
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;;
esac
fi
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off

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;

111
hwclock.c
View File

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

View File

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

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)

20
local.c
View File

@@ -563,6 +563,8 @@ void
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion)
{
LCL_CancelOffsetCorrection();
/* Dispatch to all handlers */
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
@@ -628,6 +630,24 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
/* ================================================== */
int
LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
{
ChangeListEntry *first_handler;
int r;
first_handler = change_list.next;
change_list.next = &change_list;
r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
change_list.next = first_handler;
return r;
}
/* ================================================== */
void
lcl_InvokeDispersionNotifyHandlers(double dispersion)
{

View File

@@ -173,6 +173,11 @@ extern void LCL_NotifyLeap(int leap);
a slew, in one easy step */
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
/* Same as the routine above, except it does not call the registered
parameter change handlers */
extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset,
double corr_rate);
/* Routine to read the system precision as a log to base 2 value. */
extern int LCL_GetSysPrecisionAsLog(void);

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);
}
@@ -237,6 +242,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);

15
main.c
View File

@@ -166,6 +166,8 @@ signal_cleanup(int x)
static void
quit_timeout(void *arg)
{
LOG(LOGS_INFO, "Timeout reached");
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();
@@ -329,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) {
@@ -351,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 */
@@ -635,9 +642,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;

7
ntp.h
View File

@@ -179,4 +179,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-2020
* Copyright (C) Miroslav Lichvar 2009-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
@@ -35,6 +35,7 @@
#include "ntp_ext.h"
#include "ntp_io.h"
#include "memory.h"
#include "quantiles.h"
#include "sched.h"
#include "reference.h"
#include "local.h"
@@ -63,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 */
@@ -196,8 +208,18 @@ struct NCR_Instance_Record {
SRC_Instance source;
/* Optional long-term quantile estimate of peer delay */
QNT_Instance delay_quant;
/* Optional median filter for NTP measurements */
SPF_Instance filter;
int filter_count;
/* Flag indicating HW transmit timestamps are expected */
int had_hw_tx_timestamp;
/* 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;
@@ -265,8 +287,12 @@ static ARR_Instance broadcasts;
#define MAX_MAXDELAYRATIO 1.0e6
#define MAX_MAXDELAYDEVRATIO 1.0e6
/* Parameters for the peer delay quantile */
#define DELAY_QUANT_Q 100
#define DELAY_QUANT_REPEAT 7
/* Minimum and maximum allowed poll interval */
#define MIN_POLL -6
#define MIN_POLL -7
#define MAX_POLL 24
/* Enable sub-second polling intervals only when the peer delay is not
@@ -315,9 +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);
/* ================================================== */
@@ -480,8 +512,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 */
@@ -494,11 +525,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;
}
@@ -521,6 +548,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;
}
}
/* ================================================== */
@@ -546,6 +578,16 @@ take_offline(NCR_Instance inst)
/* ================================================== */
static void
reset_report(NCR_Instance inst)
{
memset(&inst->report, 0, sizeof (inst->report));
inst->report.remote_addr = inst->remote_addr.ip_addr;
inst->report.remote_port = inst->remote_addr.port;
}
/* ================================================== */
NCR_Instance
NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, const char *name)
@@ -605,7 +647,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) {
@@ -640,27 +682,37 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
params->min_samples, params->max_samples,
params->min_delay, params->asymmetry);
if (params->max_delay_quant > 0.0) {
int k = round(CLAMP(0.05, params->max_delay_quant, 0.95) * DELAY_QUANT_Q);
result->delay_quant = QNT_CreateInstance(k, k, DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
LCL_GetSysPrecisionAsQuantum() / 2.0);
} else {
result->delay_quant = NULL;
}
if (params->filter_length >= 1)
result->filter = SPF_CreateInstance(params->filter_length, params->filter_length,
NTP_MAX_DISPERSION, 0.0);
result->filter = SPF_CreateInstance(1, params->filter_length, NTP_MAX_DISPERSION, 0.0);
else
result->filter = NULL;
result->saved_response = NULL;
result->rx_timeout_id = 0;
result->tx_timeout_id = 0;
result->tx_suspended = 1;
result->opmode = MD_OFFLINE;
result->local_poll = result->minpoll;
result->local_poll = MAX(result->minpoll, MIN_NONLAN_POLL);
result->poll_score = 0.0;
zero_local_timestamp(&result->local_tx);
result->burst_good_samples_to_go = 0;
result->burst_total_samples_to_go = 0;
memset(&result->report, 0, sizeof (result->report));
NCR_ResetInstance(result);
set_connectivity(result, params->connectivity);
reset_report(result);
return result;
}
@@ -676,9 +728,14 @@ NCR_DestroyInstance(NCR_Instance instance)
if (instance->mode == MODE_ACTIVE)
NIO_CloseServerSocket(instance->local_addr.sock_fd);
if (instance->delay_quant)
QNT_DestroyInstance(instance->delay_quant);
if (instance->filter)
SPF_DestroyInstance(instance->filter);
if (instance->saved_response)
Free(instance->saved_response);
NAU_DestroyInstance(instance->auth);
/* This will destroy the source instance inside the
@@ -732,8 +789,13 @@ NCR_ResetInstance(NCR_Instance instance)
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
zero_local_timestamp(&instance->init_local_rx);
if (instance->delay_quant)
QNT_Reset(instance->delay_quant);
if (instance->filter)
SPF_DropSamples(instance->filter);
instance->filter_count = 0;
instance->had_hw_tx_timestamp = 0;
}
/* ================================================== */
@@ -741,12 +803,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));
}
}
@@ -755,7 +819,6 @@ NCR_ResetPoll(NCR_Instance instance)
void
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
{
memset(&inst->report, 0, sizeof (inst->report));
NCR_ResetInstance(inst);
if (!ntp_only)
@@ -772,10 +835,18 @@ 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);
SRC_ResetInstance(inst->source);
reset_report(inst);
}
/* ================================================== */
@@ -783,6 +854,8 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
static void
adjust_poll(NCR_Instance inst, double adj)
{
NTP_Sample last_sample;
inst->poll_score += adj;
if (inst->poll_score >= 1.0) {
@@ -808,7 +881,9 @@ adjust_poll(NCR_Instance inst, double adj)
or it is not in a local network according to the measured delay */
if (inst->local_poll < MIN_NONLAN_POLL &&
(!SRC_IsReachable(inst->source) ||
SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY))
(SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY &&
(!inst->filter || !SPF_GetLastSample(inst->filter, &last_sample) ||
last_sample.peer_delay > MAX_LAN_PEER_DELAY))))
inst->local_poll = MIN_NONLAN_POLL;
}
@@ -862,10 +937,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.
@@ -905,12 +989,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);
@@ -928,6 +1006,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;
}
@@ -1240,6 +1324,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 */
@@ -1271,11 +1364,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;
}
@@ -1353,6 +1445,9 @@ transmit_timeout(void *arg)
}
SRC_UpdateReachability(inst->source, 0);
/* Count missing samples for the sample filter */
process_sample(inst, NULL);
}
/* With auto_offline take the source offline if sending failed */
@@ -1375,7 +1470,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
@@ -1541,6 +1636,22 @@ check_delay_ratio(NCR_Instance inst, SST_Stats stats,
/* ================================================== */
static int
check_delay_quant(NCR_Instance inst, double delay)
{
double quant;
quant = QNT_GetQuantile(inst->delay_quant, QNT_GetMinK(inst->delay_quant));
if (delay <= quant)
return 1;
DEBUG_LOG("maxdelayquant: delay=%e quant=%e", delay, quant);
return 0;
}
/* ================================================== */
static int
check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
struct timespec *sample_time, double offset, double delay)
@@ -1626,33 +1737,29 @@ check_sync_loop(NCR_Instance inst, NTP_Packet *message, NTP_Local_Address *local
static void
process_sample(NCR_Instance inst, NTP_Sample *sample)
{
double estimated_offset, error_in_estimate, filtered_sample_ago;
double estimated_offset, error_in_estimate;
NTP_Sample filtered_sample;
int filtered_samples;
/* Accumulate the sample to the median filter if it is enabled. When the
filter produces a result, check if it is not too old, i.e. the filter did
not miss too many samples due to missing responses or failing tests. */
/* Accumulate the sample to the median filter if enabled and wait for
the configured number of samples before processing (NULL indicates
a missing sample) */
if (inst->filter) {
if (sample)
SPF_AccumulateSample(inst->filter, sample);
filtered_samples = SPF_GetNumberOfSamples(inst->filter);
if (++inst->filter_count < SPF_GetMaxSamples(inst->filter))
return;
if (!SPF_GetFilteredSample(inst->filter, &filtered_sample))
return;
filtered_sample_ago = UTI_DiffTimespecsToDouble(&sample->time, &filtered_sample.time);
if (filtered_sample_ago > SOURCE_REACH_BITS / 2 * filtered_samples *
UTI_Log2ToDouble(inst->local_poll)) {
DEBUG_LOG("filtered sample dropped ago=%f poll=%d", filtered_sample_ago,
inst->local_poll);
return;
}
sample = &filtered_sample;
inst->filter_count = 0;
}
if (!sample)
return;
/* Get the estimated offset predicted from previous samples. The
convention here is that positive means local clock FAST of
reference, i.e. backwards to the way that 'offset' is defined. */
@@ -1676,7 +1783,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;
@@ -1770,8 +1939,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 &&
@@ -1787,6 +1958,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 &&
inst->had_hw_tx_timestamp && 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 &&
@@ -1907,12 +2092,17 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Test A requires that the minimum estimate of the peer delay is not
larger than the configured maximum, in both client modes that the server
processing time is sane, and in interleaved symmetric mode that the
processing time is sane, in interleaved client/server mode that the
previous response was not in basic mode (which prevents using timestamps
that minimise delay error), and in interleaved symmetric mode that the
measured delay and intervals between remote timestamps don't indicate
a missed response */
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
precision <= inst->max_delay &&
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_CLIENT && interleaved_packet &&
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
UTI_CompareTimespecs(&local_transmit.ts, &inst->local_tx.ts) == 0) &&
!(inst->mode == MODE_ACTIVE && interleaved_packet &&
(sample.peer_delay > 0.5 * prev_remote_poll_interval ||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
@@ -1925,12 +2115,18 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
administrator-defined value */
testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
/* Test C requires that the ratio of the increase in delay from the minimum
/* Test C either requires that the delay is less than an estimate of an
administrator-defined quantile, or (if the quantile is not specified)
it requires that the ratio of the increase in delay from the minimum
one in the stats data register to the standard deviation of the offsets
in the register is less than an administrator-defined value or the
difference between measured offset and predicted offset is larger than
the increase in delay */
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
if (inst->delay_quant)
testC = check_delay_quant(inst, sample.peer_delay);
else
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset,
sample.peer_delay);
/* Test D requires that the source is not synchronised to us and is not us
to prevent a synchronisation loop */
@@ -1972,7 +2168,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
updated_timestamps = 2;
/* If available, update the monotonic timestamp and accumulate the offset.
This needs to be done here to no lose changes in remote_ntp_rx in
This needs to be done here to not lose changes in remote_ntp_rx in
symmetric mode when there are multiple responses per request. */
if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) {
inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch);
@@ -2063,6 +2259,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
}
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
if (inst->delay_quant)
QNT_Accumulate(inst->delay_quant, sample.peer_delay);
}
if (good_packet) {
@@ -2088,6 +2287,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
} else {
/* Slowly increase the polling interval if we can't get a good response */
adjust_poll(inst, testD ? 0.02 : 0.1);
/* Count missing samples for the sample filter */
process_sample(inst, NULL);
}
/* If in client mode, no more packets are expected to be coming from the
@@ -2100,8 +2302,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",
@@ -2116,14 +2317,13 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
}
/* Get rid of old timeout and start a new one */
if (!saved)
assert(inst->tx_timeout_id);
restart_timeout(inst, delay_time);
}
/* Update the NTP report */
inst->report.remote_addr = inst->remote_addr.ip_addr;
inst->report.local_addr = inst->local_addr.ip_addr;
inst->report.remote_port = inst->remote_addr.port;
inst->report.leap = pkt_leap;
inst->report.version = pkt_version;
inst->report.mode = NTP_LVM_TO_MODE(message->lvm);
@@ -2148,6 +2348,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->report.rx_tss_char = tss_chars[local_receive.source];
inst->report.total_valid_count++;
if (good_packet)
inst->report.total_good_count++;
}
/* Do measurement logging */
@@ -2261,8 +2463,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. */
@@ -2305,7 +2507,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 */
@@ -2386,8 +2588,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;
@@ -2408,13 +2608,18 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
ntp_rx = message->originate_ts;
local_ntp_rx = &ntp_rx;
UTI_ZeroTimespec(&local_tx.ts);
interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts);
local_tx.source = NTP_TS_DAEMON;
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();
@@ -2431,7 +2636,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);
}
/* ================================================== */
@@ -2485,6 +2690,13 @@ NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
update_tx_timestamp(&inst->local_tx, tx_ts, &inst->local_ntp_rx, &inst->local_ntp_tx,
message);
if (tx_ts->source == NTP_TS_HARDWARE) {
inst->had_hw_tx_timestamp = 1;
if (has_saved_response(inst))
process_saved_response(inst);
}
}
/* ================================================== */
@@ -2509,7 +2721,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
@@ -2518,7 +2730,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);
}
/* ================================================== */
@@ -2541,6 +2753,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);
}
/* ================================================== */
@@ -2686,7 +2902,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);
}
@@ -2786,6 +3002,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,12 +38,6 @@ 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;

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2020
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -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 (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);
}
@@ -461,11 +464,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;
@@ -522,6 +520,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 +542,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);
@@ -594,7 +595,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message.local_addr.ip.family == IPADDR_INET4 &&
(local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4))
(bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
message.local_addr.ip.family = IPADDR_UNSPEC;
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2019
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-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
@@ -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"
@@ -59,19 +60,20 @@ struct Interface {
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
int maxpoll;
SCH_TimeoutID poll_timeout_id;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
#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
@@ -86,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
@@ -109,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;
@@ -244,12 +237,18 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
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)));
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);
@@ -414,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;
}
@@ -432,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);
}
@@ -474,73 +472,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)
{
@@ -560,26 +491,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;
double rx_correction, ts_delay, phc_err, local_err;
double rx_correction, ts_delay, local_err;
struct timespec ts;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
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
@@ -757,11 +732,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) &&
@@ -824,23 +794,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

@@ -35,13 +35,9 @@ extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
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

@@ -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"
@@ -64,6 +65,7 @@ typedef struct {
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 +98,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 +108,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 +189,7 @@ void
NSR_Initialise(void)
{
n_sources = 0;
resolving_id = 0;
initialised = 1;
records = ARR_CreateInstance(sizeof (SourceRecord));
@@ -206,6 +212,7 @@ NSR_Finalise(void)
ARR_DestroyInstance(records);
ARR_DestroyInstance(pools);
SCH_RemoveTimeout(resolving_id);
while (unresolved_sources)
remove_unresolved_source(unresolved_sources);
@@ -317,6 +324,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,
@@ -353,13 +385,13 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
record_lock = 1;
record = get_record(slot);
assert(!name || !UTI_IsStringIP(name));
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->pool_id = pool_id;
record->tentative = 1;
record->conf_id = conf_id;
record->last_resolving = SCH_GetLastEventMonoTime();
record_lock = 0;
@@ -372,6 +404,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();
@@ -491,6 +525,19 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
unsigned short first = 0;
int i, j;
/* 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 (UTI_CompareIPs(&us->address.ip_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));
@@ -698,21 +745,25 @@ static int get_unused_pool_id(void)
/* ================================================== */
static uint32_t
get_next_conf_id(uint32_t *conf_id)
{
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
return last_conf_id;
}
/* ================================================== */
NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
NSR_Status s;
s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
if (s != NSR_Success)
return s;
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
return s;
return add_source(remote_addr, NULL, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
/* ================================================== */
@@ -725,11 +776,13 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
struct SourcePool *sp;
NTP_Remote_Address remote_addr;
int i, new_sources, pool_id;
uint32_t cid;
/* If the name is an IP address, add the source with the address directly */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
return NSR_AddSource(&remote_addr, type, params, conf_id);
return add_source(&remote_addr, name, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
/* Make sure the name is at least printable and has no spaces */
@@ -741,6 +794,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;
@@ -770,14 +824,12 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
append_unresolved_source(us);
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
cid = get_next_conf_id(conf_id);
for (i = 0; i < new_sources; i++) {
if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id;
if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success)
if (add_source(&remote_addr, name, type, params, us->pool_id, cid) != NSR_Success)
return NSR_TooManySources;
}
@@ -881,6 +933,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.
@@ -903,6 +956,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);
}
@@ -930,20 +984,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;
@@ -956,11 +1013,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))
@@ -975,15 +1032,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);
}
/* ================================================== */
@@ -999,7 +1097,7 @@ NSR_RefreshAddresses(void)
if (!record->remote_addr)
continue;
resolve_source_replacement(record);
resolve_source_replacement(record, 1);
}
}
@@ -1100,8 +1198,10 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
assert(initialised);
/* Must match IP address AND port number */
if (find_slot2(remote_addr, &slot) == 2) {
/* Avoid unnecessary lookup if the packet cannot be a response from our
source. Otherwise, it must match both IP address and port number. */
if (NTP_LVM_TO_MODE(message->lvm) != MODE_CLIENT &&
find_slot2(remote_addr, &slot) == 2) {
record = get_record(slot);
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
@@ -1123,6 +1223,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);
}
@@ -1137,8 +1239,10 @@ NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
SourceRecord *record;
int slot;
/* Must match IP address AND port number */
if (find_slot2(remote_addr, &slot) == 2) {
/* Avoid unnecessary lookup if the packet cannot be a request to our
source. Otherwise, it must match both IP address and port number. */
if (NTP_LVM_TO_MODE(message->lvm) != MODE_SERVER &&
find_slot2(remote_addr, &slot) == 2) {
record = get_record(slot);
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
} else {

View File

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

@@ -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;
memset(new_keys[i].key + key_length, 0, sizeof (new_keys[i].key) - key_length);
}
if (i < MAX_SERVER_KEYS)
goto error;
index = id % MAX_SERVER_KEYS;
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));
server_keys[index].id = id;
assert(sizeof (server_keys[index].key) == sizeof (key));
memcpy(server_keys[index].key, key, key_length);
update_key_siv(&server_keys[index], new_keys[i].siv_algorithm);
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);
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_ERR, "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;
@@ -759,7 +804,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 +824,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 +902,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 +912,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 +927,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 +939,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 +959,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 +976,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 +985,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 +1009,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
return 0;
}
/* 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

@@ -594,13 +594,13 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
static int gnutls_initialised = 0;
static void
static int
init_gnutls(void)
{
int r;
if (gnutls_initialised)
return;
return 1;
r = gnutls_global_init();
if (r < 0)
@@ -611,8 +611,12 @@ init_gnutls(void)
r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
if (r < 0) {
LOG(LOGS_ERR, "Could not initialise %s : %s",
"priority cache for TLS", gnutls_strerror(r));
gnutls_global_deinit();
return 0;
}
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time);
@@ -621,6 +625,8 @@ init_gnutls(void)
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL);
return 1;
}
/* ================================================== */
@@ -649,7 +655,8 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
gnutls_certificate_credentials_t credentials = NULL;
int i, r;
init_gnutls();
if (!init_gnutls())
return NULL;
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
@@ -660,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

@@ -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 {

209
quantiles.c Normal file
View File

@@ -0,0 +1,209 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Estimation of quantiles using the Frugal-2U streaming algorithm
(https://arxiv.org/pdf/1407.1121v1.pdf)
*/
#include "config.h"
#include "logging.h"
#include "memory.h"
#include "quantiles.h"
#include "regress.h"
#include "util.h"
/* Maximum number of repeated estimates for stabilisation */
#define MAX_REPEAT 64
struct Quantile {
double est;
double step;
int sign;
};
struct QNT_Instance_Record {
struct Quantile *quants;
int n_quants;
int repeat;
int q;
int min_k;
double min_step;
int n_set;
};
/* ================================================== */
QNT_Instance
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
{
QNT_Instance inst;
long seed;
if (q < 2 || min_k > max_k || min_k < 1 || max_k >= q ||
repeat < 1 || repeat > MAX_REPEAT || min_step <= 0.0)
assert(0);
inst = MallocNew(struct QNT_Instance_Record);
inst->n_quants = (max_k - min_k + 1) * repeat;
inst->quants = MallocArray(struct Quantile, inst->n_quants);
inst->repeat = repeat;
inst->q = q;
inst->min_k = min_k;
inst->min_step = min_step;
QNT_Reset(inst);
/* Seed the random number generator, which will not be isolated from
other instances and other random() users */
UTI_GetRandomBytes(&seed, sizeof (seed));
srandom(seed);
return inst;
}
/* ================================================== */
void
QNT_DestroyInstance(QNT_Instance inst)
{
Free(inst->quants);
Free(inst);
}
/* ================================================== */
void
QNT_Reset(QNT_Instance inst)
{
int i;
inst->n_set = 0;
for (i = 0; i < inst->n_quants; i++) {
inst->quants[i].est = 0.0;
inst->quants[i].step = inst->min_step;
inst->quants[i].sign = 1;
}
}
/* ================================================== */
static void
insert_initial_value(QNT_Instance inst, double value)
{
int i, j, r = inst->repeat;
if (inst->n_set * r >= inst->n_quants)
assert(0);
/* Keep the initial estimates repeated and ordered */
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = inst->quants[(i - 1) * r].est;
}
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = value;
inst->n_set++;
/* Duplicate the largest value in unset quantiles */
for (i = inst->n_set * r; i < inst->n_quants; i++)
inst->quants[i].est = inst->quants[i - 1].est;
}
/* ================================================== */
static void
update_estimate(struct Quantile *quantile, double value, double p, double rand,
double min_step)
{
if (value > quantile->est && rand > (1.0 - p)) {
quantile->step += quantile->sign > 0 ? min_step : -min_step;
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est > value) {
quantile->step += value - quantile->est;
quantile->est = value;
}
if (quantile->sign < 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = 1;
} else if (value < quantile->est && rand > p) {
quantile->step += quantile->sign < 0 ? min_step : -min_step;
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est < value) {
quantile->step += quantile->est - value;
quantile->est = value;
}
if (quantile->sign > 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = -1;
}
}
/* ================================================== */
void
QNT_Accumulate(QNT_Instance inst, double value)
{
double p, rand;
int i;
/* Initialise the estimates with first received values */
if (inst->n_set * inst->repeat < inst->n_quants) {
insert_initial_value(inst, value);
return;
}
for (i = 0; i < inst->n_quants; i++) {
p = (double)(i / inst->repeat + inst->min_k) / inst->q;
rand = (double)random() / ((1U << 31) - 1);
update_estimate(&inst->quants[i], value, p, rand, inst->min_step);
}
}
/* ================================================== */
int
QNT_GetMinK(QNT_Instance inst)
{
return inst->min_k;
}
/* ================================================== */
double
QNT_GetQuantile(QNT_Instance inst, int k)
{
double estimates[MAX_REPEAT];
int i;
if (k < inst->min_k || k - inst->min_k >= inst->n_quants)
assert(0);
for (i = 0; i < inst->repeat; i++)
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
return RGR_FindMedian(estimates, inst->repeat);
}

41
quantiles.h Normal file
View File

@@ -0,0 +1,41 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for estimation of quantiles.
*/
#ifndef GOT_QUANTILES_H
#define GOT_QUANTILES_H
typedef struct QNT_Instance_Record *QNT_Instance;
extern QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step);
extern void QNT_DestroyInstance(QNT_Instance inst);
extern void QNT_Reset(QNT_Instance inst);
extern void QNT_Accumulate(QNT_Instance inst, double value);
extern int QNT_GetMinK(QNT_Instance inst);
extern double QNT_GetQuantile(QNT_Instance inst, int k);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019, 2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -55,21 +55,6 @@ struct FilterSample {
struct timespec sample_time;
};
struct MedianFilter {
int length;
int index;
int used;
int last;
int avg_var_n;
double avg_var;
double max_var;
struct FilterSample *samples;
int *selected;
double *x_data;
double *y_data;
double *w_data;
};
struct RCL_Instance_Record {
RefclockDriver *driver;
void *data;
@@ -79,6 +64,7 @@ struct RCL_Instance_Record {
int driver_polled;
int poll;
int leap_status;
int local;
int pps_forced;
int pps_rate;
int pps_active;
@@ -190,6 +176,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->poll = params->poll;
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
inst->local = params->local;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
@@ -231,6 +218,13 @@ RCL_AddRefclock(RefclockParameters *params)
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
}
if (inst->local) {
inst->pps_forced = 1;
inst->lock_ref = inst->ref_id;
inst->leap_status = LEAP_Unsynchronised;
inst->max_lock_age = MAX(inst->max_lock_age, 3);
}
if (inst->driver->poll) {
int max_samples;
@@ -300,7 +294,7 @@ RCL_StartRefclocks(void)
break;
}
if (lock_index == -1 || lock_index == i)
if (lock_index == -1 || (lock_index == i && !inst->local))
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
}
@@ -440,20 +434,24 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
}
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap)
{
double correction, dispersion;
double correction, dispersion, raw_offset, offset;
struct timespec cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time, -offset);
return RCL_AddPulse(instance, sample_time,
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
if (!UTI_IsTimeOffsetSane(sample_time, raw_offset) ||
!valid_sample_time(instance, &cooked_time))
return 0;
@@ -468,18 +466,24 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
return 0;
}
/* Calculate offset = raw_offset - correction + instance->offset
in parts to avoid loss of precision if there are large differences */
offset = ref_time->tv_sec - sample_time->tv_sec -
(time_t)correction + (time_t)instance->offset;
offset += 1.0e-9 * (ref_time->tv_nsec - sample_time->tv_nsec) -
(correction - (time_t)correction) + (instance->offset - (time_t)instance->offset);
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
DEBUG_LOG("refclock sample ignored unknown TAI offset");
return 0;
}
if (!accumulate_sample(instance, &cooked_time,
offset - correction + instance->offset, dispersion))
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
return 0;
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
/* for logging purposes */
if (!instance->driver->poll)
@@ -558,16 +562,30 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
lock_refclock = get_refclock(instance->lock_ref);
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
if (instance->local) {
/* Make the first sample in order to lock to itself */
ref_sample.time = *cooked_time;
ref_sample.offset = offset;
ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
ref_sample.root_delay = ref_sample.root_dispersion = 0;
} else {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
}
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff);
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", sample_diff);
/* Restart the local mode */
if (instance->local) {
LOG(LOGS_WARN, "Local refclock lost lock");
SPF_DropSamples(instance->filter);
SRC_ResetInstance(instance->source);
}
return 0;
}
@@ -693,6 +711,52 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
return 0;
}
static void
get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
{
double offset_sd, freq_sd, skew, root_delay, root_disp;
SST_Stats stats = SRC_GetSourcestats(inst->source);
if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
UTI_ZeroTimespec(ref);
return;
}
SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
&skew, &root_delay, &root_disp);
}
static void
follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
double prev_offset)
{
SST_Stats stats = SRC_GetSourcestats(inst->source);
double freq, dfreq, offset, doffset, elapsed;
struct timespec now, ref_time;
get_local_stats(inst, &ref_time, &freq, &offset);
if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
return;
dfreq = (freq - prev_freq) / (1.0 - prev_freq);
elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
doffset = offset - elapsed * prev_freq - prev_offset;
if (!REF_AdjustReference(doffset, dfreq))
return;
LCL_ReadCookedTime(&now, NULL);
SST_SlewSamples(stats, &now, dfreq, doffset);
SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
/* Keep the offset close to zero to not lose precision */
if (fabs(offset) >= 1.0) {
SST_CorrectOffset(stats, -round(offset));
SPF_CorrectOffset(inst->filter, -round(offset));
}
}
static void
poll_timeout(void *arg)
{
@@ -713,17 +777,28 @@ poll_timeout(void *arg)
inst->driver_polled = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) {
double local_freq, local_offset;
struct timespec local_ref_time;
/* Handle special case when PPS is used with the local reference */
if (inst->pps_active && inst->lock_ref == -1)
stratum = pps_stratum(inst, &sample.time);
else
stratum = inst->stratum;
if (inst->local) {
get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
inst->leap_status = LEAP_Unsynchronised;
}
SRC_UpdateReachability(inst->source, 1);
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);
if (inst->local)
follow_local(inst, &local_ref_time, local_freq, local_offset);
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);

View File

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

View File

@@ -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,9 +80,15 @@ 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));
if (phc->extpps) {
s = RCL_GetDriverOption(instance, "pin");
@@ -81,16 +96,18 @@ static int phc_initialise(RCL_Instance instance)
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))
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;
phc->clock = NULL;
}
RCL_SetDriverData(instance, phc);
@@ -100,68 +117,106 @@ 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);
HCL_DestroyInstance(phc->clock);
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);
close(phc->fd);
Free(phc);
}
static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
{
struct phc_instance *phc;
struct timespec local_ts;
double local_err;
phc = RCL_GetDriverData(instance);
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))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
}
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
double local_err;
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;
phc = RCL_GetDriverData(instance);
phc1 = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
/* 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. */
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return;
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);
}
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));
}
#define PHC_READINGS 25
static int phc_poll(RCL_Instance instance)
{
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
struct phc_instance *phc;
struct timespec phc_ts, sys_ts, local_ts;
double offset, phc_err, local_err;
double phc_err, local_err;
int n_readings;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &phc_err))
n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
PHC_READINGS, readings);
if (n_readings < 1)
return 0;
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
return 0;
if (phc->extpps) {
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (phc->extpps)
return 0;
}
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {

View File

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

View File

@@ -58,23 +58,63 @@ 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;
struct timespec ts;
RCL_Instance instance;
int s;
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;
@@ -86,13 +126,18 @@ static void read_sample(int sockfd, int event, void *anything)
return;
}
UTI_TimevalToTimespec(&sample.tv, &ts);
UTI_NormaliseTimespec(&ts);
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&sys_ts);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return;
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
if (sample.pulse) {
RCL_AddPulse(instance, &ts, sample.offset);
RCL_AddPulse(instance, &sys_ts, sample.offset);
} else {
RCL_AddSample(instance, &ts, sample.offset, sample.leap);
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
}
}

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018, 2020
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -150,6 +150,9 @@ static SCH_TimeoutID fb_drift_timeout_id;
static double last_ref_update;
static double last_ref_update_interval;
static double last_ref_adjustment;
static int ref_adjustments;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
@@ -286,6 +289,8 @@ REF_Initialise(void)
UTI_ZeroTimespec(&our_ref_time);
last_ref_update = 0.0;
last_ref_update_interval = 0.0;
last_ref_adjustment = 0.0;
ref_adjustments = 0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -960,6 +965,27 @@ fuzz_ref_time(struct timespec *ts)
/* ================================================== */
static double
get_correction_rate(double offset_sd, double update_interval)
{
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
return correction_time_ratio * 0.5 * offset_sd * update_interval;
}
/* ================================================== */
void
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
@@ -969,7 +995,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
{
double uncorrected_offset, accumulate_offset, step_offset;
double residual_frequency, local_abs_frequency;
double elapsed, mono_now, update_interval, correction_rate, orig_root_distance;
double elapsed, mono_now, update_interval, orig_root_distance;
struct timespec now, raw_now;
int manual;
@@ -1024,21 +1050,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
last_ref_update_interval = update_interval;
last_offset = offset;
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */
if (is_step_limit_reached(offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */
@@ -1050,7 +1061,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
}
/* Adjust the clock */
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset,
get_correction_rate(offset_sd, update_interval));
maybe_log_offset(offset, raw_now.tv_sec);
@@ -1095,6 +1107,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
avg2_moving = 1;
avg2_offset = SQUARE(offset);
}
ref_adjustments = 0;
}
/* ================================================== */
int
REF_AdjustReference(double offset, double frequency)
{
double adj_corr_rate, ref_corr_rate, mono_now;
mono_now = SCH_GetLastEventMonoTime();
ref_adjustments++;
adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment);
ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) /
ref_adjustments;
last_ref_adjustment = mono_now;
return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset,
MAX(adj_corr_rate, ref_corr_rate));
}
/* ================================================== */
@@ -1296,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);
}
/* ================================================== */
@@ -1305,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);
}
/* ================================================== */
@@ -1316,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");
}
/* ================================================== */
@@ -1324,6 +1360,7 @@ void
REF_DisableLocal(void)
{
enable_local_stratum = 0;
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
}
/* ================================================== */

View File

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

View File

@@ -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 {
@@ -174,6 +180,7 @@ typedef struct {
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
} RPT_NTPReport;
typedef struct {

View File

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

View File

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

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

@@ -165,17 +165,29 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
datum.data = (unsigned char *)key;
datum.size = length;
/* Initialise a new cipher with the provided key (gnutls does not seem to
have a function to change the key directly) */
#ifdef HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
if (instance->cipher) {
r = gnutls_aead_cipher_set_key(instance->cipher, &datum);
if (r < 0) {
DEBUG_LOG("Could not set cipher key : %s", gnutls_strerror(r));
return 0;
}
return 1;
}
#endif
/* Initialise a new cipher with the provided key */
r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
return 0;
}
/* Replace the previous cipher */
/* Destroy the previous cipher (if its key could not be changed directly) */
if (instance->cipher)
gnutls_aead_cipher_deinit(instance->cipher);
instance->cipher = cipher;
return 1;
@@ -183,6 +195,22 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
/* ================================================== */
int
SIV_GetMinNonceLength(SIV_Instance instance)
{
return 1;
}
/* ================================================== */
int
SIV_GetMaxNonceLength(SIV_Instance instance)
{
return INT_MAX;
}
/* ================================================== */
int
SIV_GetTagLength(SIV_Instance instance)
{

View File

@@ -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;
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,
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,
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

View File

@@ -505,6 +505,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
{
union sockaddr_all saddr;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr);
@@ -537,6 +539,8 @@ connect_unix_address(int sock_fd, const char *addr)
{
union sockaddr_all saddr;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr);

185
sources.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2021
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -68,6 +68,7 @@ struct SelectInfo {
typedef enum {
SRC_OK, /* OK so far, not a final status! */
SRC_UNSELECTABLE, /* Has noselect option set */
SRC_UNSYNCHRONISED, /* Provides samples but not unsynchronised */
SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
@@ -111,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;
@@ -139,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;
};
/* ================================================== */
@@ -164,6 +172,8 @@ 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 */
/* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0
@@ -171,12 +181,19 @@ 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 */
#define DUMP_IDENTIFIER "SRC0\n"
@@ -188,6 +205,7 @@ static void slew_sources(struct timespec *raw, struct timespec *cooked, double d
double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything);
static char *source_to_string(SRC_Instance inst);
static char get_status_char(SRC_Status status);
/* ================================================== */
/* Initialisation function */
@@ -207,6 +225,12 @@ 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;
}
/* ================================================== */
@@ -287,7 +311,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;
@@ -316,11 +346,13 @@ 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));
@@ -460,6 +492,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)
{
@@ -480,14 +525,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);
}
/* ================================================== */
@@ -572,17 +612,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;
@@ -594,7 +634,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);
}
/* ================================================== */
@@ -639,12 +679,44 @@ source_to_string(SRC_Instance inst)
static void
mark_source(SRC_Instance inst, SRC_Status status)
{
struct timespec now;
inst->status = status;
DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options,
(unsigned int)inst->reachability, inst->reachability_size, inst->updates,
inst->distant, (int)inst->leap, inst->leap_vote,
/* 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, inst->bad, (int)inst->leap, inst->leap_vote,
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
if (logfileid == -1)
return;
SCH_GetLastEventTime(&now, NULL, NULL);
LOG_FileWrite(logfileid,
"%s %-15s %c -%c%c%c%c %4o %5.2f %10.3e %10.3e %10.3e",
UTI_TimeToLogForm(now.tv_sec), source_to_string(inst),
get_status_char(inst->status),
inst->sel_options & SRC_SELECT_NOSELECT ? 'N' : '-',
inst->sel_options & SRC_SELECT_PREFER ? 'P' : '-',
inst->sel_options & SRC_SELECT_TRUST ? 'T' : '-',
inst->sel_options & SRC_SELECT_REQUIRE ? 'R' : '-',
(unsigned int)inst->reachability, inst->sel_score,
inst->sel_info.last_sample_ago,
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
}
@@ -722,8 +794,8 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / SQUARE(src_frequency_sd);
DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd,
DEBUG_LOG("combining %s oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
source_to_string(sources[index]), offset_weight, src_offset, src_offset_sd,
frequency_weight, src_frequency, src_frequency_sd, src_skew);
sum_offset_weight += offset_weight;
@@ -773,13 +845,15 @@ 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);
log_selection_message(LOGS_INFO, "Can't synchronise: no sources", NULL);
selected_source_index = INVALID_SOURCE;
}
return;
@@ -815,6 +889,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* Ignore sources which are not synchronised */
if (sources[i]->leap == LEAP_Unsynchronised) {
mark_source(sources[i], SRC_UNSYNCHRONISED);
continue;
}
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now,
&si->lo_limit, &si->hi_limit, &si->root_distance,
@@ -966,7 +1046,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
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);
log_selection_message(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
selected_source_index = INVALID_SOURCE;
}
return;
@@ -1046,8 +1126,12 @@ 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;
}
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no majority", NULL);
REF_SetUnsynchronised();
selected_source_index = INVALID_SOURCE;
}
@@ -1093,12 +1177,16 @@ 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",
log_selection_message(LOGS_INFO, "Can't synchronise: %s selectable sources",
!n_sel_sources ? "no" :
sel_req_source ? "no required source in" : "not enough");
selected_source_index = INVALID_SOURCE;
@@ -1215,13 +1303,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
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;
}
mark_source(sources[selected_source_index], SRC_SELECTED);
@@ -1512,6 +1603,8 @@ SRC_ResetSources(void)
for (i = 0; i < n_sources; i++)
SRC_ResetInstance(sources[i]);
LOG(LOGS_INFO, "Reset all sources");
}
/* ================================================== */
@@ -1559,6 +1652,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)
{
@@ -1642,6 +1775,8 @@ get_status_char(SRC_Status status)
switch (status) {
case SRC_UNSELECTABLE:
return 'N';
case SRC_UNSYNCHRONISED:
return 's';
case SRC_BAD_STATS:
return 'M';
case SRC_BAD_DISTANCE:

View File

@@ -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;
@@ -211,8 +211,8 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_sample
SST_Stats inst;
inst = MallocNew(struct SST_Stats_Record);
inst->min_samples = min_samples;
inst->max_samples = max_samples;
inst->max_samples = max_samples > 0 ? CLAMP(1, max_samples, MAX_SAMPLES) : MAX_SAMPLES;
inst->min_samples = CLAMP(1, min_samples, inst->max_samples);
inst->fixed_min_delay = min_delay;
inst->fixed_asymmetry = asymmetry;
@@ -698,7 +698,8 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
/* If maxsamples is too small to have a successful regression, enable the
selection as a special case for a fast update/print-once reference mode */
if (!*select_ok && inst->n_samples < 3 && inst->n_samples == inst->max_samples) {
if (!*select_ok && inst->n_samples < MIN_SAMPLES_FOR_REGRESS &&
inst->n_samples == inst->max_samples) {
*std_dev = CNF_GetMaxJitter();
*select_ok = 1;
}
@@ -814,7 +815,7 @@ SST_PredictOffset(SST_Stats inst, struct timespec *when)
{
double elapsed;
if (inst->n_samples < 3) {
if (inst->n_samples < MIN_SAMPLES_FOR_REGRESS) {
/* We don't have any useful statistics, and presumably the poll
interval is minimal. We can't do any useful prediction other
than use the latest sample or zero if we don't have any samples */
@@ -930,6 +931,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
/* Make sure the samples are sane and they are in order */
if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
UTI_CompareTimespecs(&now, &inst->sample_times[i]) < 0 ||
!(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 &&
fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) ||
(i > 0 && UTI_CompareTimespecs(&inst->sample_times[i],
@@ -962,8 +964,9 @@ 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];
if (inst->ip_addr)
last_sample_time.tv_nsec = 0;
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else {
@@ -985,6 +988,14 @@ SST_Samples(SST_Stats inst)
/* ================================================== */
int
SST_GetMinSamples(SST_Stats inst)
{
return inst->min_samples;
}
/* ================================================== */
void
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
{

View File

@@ -133,6 +133,8 @@ extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *repor
extern int SST_Samples(SST_Stats inst);
extern int SST_GetMinSamples(SST_Stats inst);
extern double SST_GetJitterAsymmetry(SST_Stats inst);
#endif /* GOT_SOURCESTATS_H */

View File

@@ -61,6 +61,7 @@ typedef struct {
double max_delay;
double max_delay_ratio;
double max_delay_dev_ratio;
double max_delay_quant;
double min_delay;
double asymmetry;
double offset;

View File

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

View File

@@ -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,12 @@ 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
SCMP_SYS(rt_sigaction),
SCMP_SYS(rt_sigreturn),
SCMP_SYS(rt_sigprocmask),
@@ -596,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),
@@ -630,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 },
@@ -647,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
@@ -791,73 +802,25 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#define PHC_READINGS 10
static int
process_phc_readings(struct timespec ts[][3], int n, double precision,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
get_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
{
double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
int i, combined;
if (n > PTP_MAX_SAMPLES)
return 0;
for (i = 0; i < n; i++) {
delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
if (delays[i] < 0.0) {
/* Step in the middle of a PHC reading? */
DEBUG_LOG("Bad PTP_SYS_OFFSET sample delay=%e", delays[i]);
return 0;
}
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
sys_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
if (delays[i] > min_delay + MAX(sys_prec, precision))
continue;
phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
combined++;
}
assert(combined);
UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
*err = MAX(min_delay / 2.0, precision);
return 1;
}
/* ================================================== */
static int
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
struct timespec ts[PHC_READINGS][3];
struct ptp_sys_offset sys_off;
int i;
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS;
sys_off.n_samples = max_samples;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
return 0;
}
for (i = 0; i < PHC_READINGS; i++) {
for (i = 0; i < max_samples; i++) {
ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
@@ -866,31 +829,31 @@ get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
}
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
return max_samples;
}
/* ================================================== */
static int
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
get_extended_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
{
#ifdef PTP_SYS_OFFSET_EXTENDED
struct timespec ts[PHC_READINGS][3];
struct ptp_sys_offset_extended sys_off;
int i;
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS;
sys_off.n_samples = max_samples;
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
return 0;
}
for (i = 0; i < PHC_READINGS; i++) {
for (i = 0; i < max_samples; i++) {
ts[i][0].tv_sec = sys_off.ts[i][0].sec;
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
@@ -899,7 +862,7 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
}
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
return max_samples;
#else
return 0;
#endif
@@ -908,12 +871,14 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
/* ================================================== */
static int
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
{
#ifdef PTP_SYS_OFFSET_PRECISE
struct ptp_sys_offset_precise sys_off;
if (max_samples < 1)
return 0;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
@@ -923,11 +888,11 @@ get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
return 0;
}
phc_ts->tv_sec = sys_off.device.sec;
phc_ts->tv_nsec = sys_off.device.nsec;
sys_ts->tv_sec = sys_off.sys_realtime.sec;
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
ts[0][0].tv_sec = sys_off.sys_realtime.sec;
ts[0][0].tv_nsec = sys_off.sys_realtime.nsec;
ts[0][1].tv_sec = sys_off.device.sec;
ts[0][1].tv_nsec = sys_off.device.nsec;
ts[0][2] = ts[0][0];
return 1;
#else
@@ -971,23 +936,23 @@ SYS_Linux_OpenPHC(const char *path, int phc_index)
/* ================================================== */
int
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec tss[][3])
{
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
int r = 0;
if ((*reading_mode == 2 || *reading_mode == 0) && !nocrossts &&
(r = get_precise_phc_readings(fd, max_readings, tss)) > 0) {
*reading_mode = 2;
return 1;
} else if ((*reading_mode == 3 || !*reading_mode) &&
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
} else if ((*reading_mode == 3 || *reading_mode == 0) &&
(r = get_extended_phc_readings(fd, max_readings, tss)) > 0) {
*reading_mode = 3;
return 1;
} else if ((*reading_mode == 1 || !*reading_mode) &&
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
} else if ((*reading_mode == 1 || *reading_mode == 0) &&
(r = get_phc_readings(fd, max_readings, tss)) > 0) {
*reading_mode = 1;
return 1;
}
return 0;
return r;
}
/* ================================================== */
@@ -1005,7 +970,7 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
pin_desc.chan = channel;
if (ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
if (pin >= 0 && ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
return 0;
}
@@ -1034,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

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

View File

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

View File

@@ -2,6 +2,8 @@
# Run the unit and simulation tests with different compiler sanitizers
# and under valgrind
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite"
cd ../..
if [ "$(uname -sm)" != "Linux x86_64" ]; then
@@ -24,6 +26,7 @@ for extra_config_opts in \
"--all-privops" \
"--disable-ipv6" \
"--disable-scfilter" \
"--without-aes-gcm-siv" \
"--without-gnutls" \
"--without-nettle" \
"--without-nettle --without-nss" \
@@ -42,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
@@ -75,7 +78,7 @@ do
pushd test/unit || exit 1
make "$@" || exit 1
if [ "$san_options" = "" ]; then
make check TEST_WRAPPER="valgrind --error-exitcode=1" || exit 1
make check TEST_WRAPPER="valgrind $valgrind_opts --error-exitcode=1" || exit 1
else
make check || exit 1
fi
@@ -87,7 +90,7 @@ do
pushd test/simulation || exit 1
export CLKNETSIM_RANDOM_SEED=101
if [ "$arch_opts" = "" -a "$san_options" = "" ]; then
CLKNETSIM_CLIENT_WRAPPER=valgrind ./run -i 1 || exit 1
CLKNETSIM_CLIENT_WRAPPER="valgrind $valgrind_opts" ./run -i 1 || exit 1
elif [ "$CC" = "gcc" ] && ! echo $CFLAGS | grep -q "-static-libasan"; then
libasan=$(ldd ../../chronyd | grep -o '/.*lib.*/libasan.so.[0-9]')
CLKNETSIM_PRELOAD=$libasan ./run -i 1 || exit 1

View File

@@ -29,7 +29,7 @@ echo "$ntp_start" | grep -q '-' && test_skip
for time_offset in -1e-1 1e-1; do
for start_offset in 0 "2^32 - $limit"; do
export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
@@ -38,7 +38,7 @@ for time_offset in -1e-1 1e-1; do
done
for start_offset in -$limit "2^32"; do
export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail

View File

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

View File

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

View File

@@ -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 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" \
"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" \
@@ -231,6 +246,7 @@ RX timestamping : Kernel
Total TX : 1
Total RX : 1
Total valid RX : 1
Total good RX : 0
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
@@ -244,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
@@ -326,6 +348,8 @@ 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
delete 192.168.123.1"
run_test || test_fail
@@ -344,6 +368,10 @@ 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$" || test_fail
chronyc_conf="

View File

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

View File

@@ -15,10 +15,11 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
check_file_messages "111 111 .111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
rm -f tmp/measurements.log
server_conf=""
max_sync_time=270
run_test || test_fail
check_chronyd_exit || test_fail
@@ -28,6 +29,7 @@ check_sync || test_fail
check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail
check_file_messages "111 111 0111.* 4I [DKH] [DKH]\$" 1 1 measurements.log || test_fail
rm -f tmp/measurements.log
clients=2

View File

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

View File

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

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

View File

@@ -160,8 +160,8 @@ for dns in 1 0; do
check_file_messages " 2 1 .* 4460 " 50 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" 4 10 || test_fail
servers=2

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

@@ -78,6 +78,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
@@ -469,6 +470,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 +513,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
@@ -95,12 +96,13 @@ TX timestamping : (Daemon|Kernel)
RX timestamping : (Daemon|Kernel)
Total TX : [0-9]+
Total RX : [0-9]+
Total valid RX : [0-9]+$" || test_fail
Total valid RX : [0-9]+
Total good RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
s 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+
@@ -113,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

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

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"

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