Compare commits

...

142 Commits

Author SHA1 Message Date
Miroslav Lichvar
6fe4a60a1d client: fix typo in selectdata help text 2025-11-04 14:47:35 +01:00
Miroslav Lichvar
2da8fbc4c3 sources: add configurable limits for stratum
Add minstratum and maxstratum directives to specify the minimum and
maximum allowed stratum of sources to be selected. The default values
are 0 and 15 respectively, allowing all NTP sources and refclocks.

Sources that are rejected due to having too large or too small stratum
are marked with 'r' in the selection log and selectdata report.

This is similar to the "tos floor" and "tos ceiling" settings of ntpd,
except that maxstratum is interpreted as one below the ceiling.
2025-11-04 14:47:35 +01:00
Miroslav Lichvar
c252e52ee2 sources: rename min_stratum in SRC_SelectSource()
Rename the variable to min_sel_stratum to make it more specific and
avoid shadowing a variable that will be added in the next commit.
2025-11-04 14:47:35 +01:00
Miroslav Lichvar
c8a9ca4cf0 sources: don't save SST_GetSelectionData() status
There is no need to save the SST_GetSelectionData() "select_ok" status
as the source is immediately marked as SRC_BAD_STATS if it is not ok.
Nothing else is using this information.
2025-11-04 14:47:35 +01:00
Miroslav Lichvar
4659b574bf sourcestats: change ST_GetSelectionData() to return status directly
For better consistency, indicate success (usable data) by returning 1
instead of setting a pointer parameter.
2025-11-04 14:47:35 +01:00
Miroslav Lichvar
5436618c05 sourcestats: drop disabled code in SST_GetSelectionData() 2025-11-04 14:47:35 +01:00
Miroslav Lichvar
a0f460569e test: make 007-cmdmon test even more reliable
The last hit value in the clients report may be "-", because there is
a backward step of the clock caused by the doffset and reset sources
commands.
2025-11-04 14:44:09 +01:00
Miroslav Lichvar
3c39afa13c sys_linux: fix building with older compilers and some archs
The recent replacement of <termios.h> with <linux/termios.h> to get
TCGETS2 seems to work only with compilers (or C standards) that allow
the same structure to be defined multiple times. There is a conflict
between <sys/ioctl.h> and <linux/termios.h>.

Another problem is that TCGETS2 is not used on some archs like ppc64.

Switch back to <termios.h> and move TCGETS2 to a list in a separate
file where it can be compiled without <sys/ioctl.h>.

Fixes: 03875f1ea5 ("sys_linux: allow ioctl(TCGETS2) in seccomp filter")
2025-10-22 14:03:44 +02:00
Miroslav Lichvar
03875f1ea5 sys_linux: allow ioctl(TCGETS2) in seccomp filter
Add TCGETS2 to the list of allowed ioctls. It seems to be called by the
latest glibc version from isatty(), which is called from libpcsclite
used by gnutls in an NTS-KE session.

Include the linux termios header instead of glibc header to get a usable
definition of TCGETS2.
2025-10-21 14:22:08 +02:00
Miroslav Lichvar
2e29935c54 local: improve measurement of clock precision
By default, the clock precision is set to the minimum measured time
needed to read the clock. This value is typically larger than the actual
resolution, which causes the NTP server to add more noise to NTP
timestamps than necessary. With HW timestamping and PTP corrections
enabled by the NTP-over-PTP transport that can be the limiting factor in
the stability of NTP measurements.

Try to determine the actual resolution of the clock. On non-Linux
systems use the clock_getres() function. On FreeBSD and NetBSD it seems
to provide expected values. On illumos it returns a large value (kernel
tick length?). On Linux it seems to be the internal timer resolution,
which is 1 ns with hrtimers, even when using a lower-resolution
clocksource like hpet or acpi_pm.

On Linux, try to measure the resolution as the minimum observed change
in differences between consecutive readings of the CLOCK_MONOTONIC_RAW
clock with a varying amount of busy work. Ignore 1ns changes due to
the kernel converting readings to timespec. This seems to work reliably.
In a test with the acpi_pm clocksource, differences of 3073, 3352, and
3631 ns were measured, which gives a resolution of 279 ns, matching the
clocksource frequency of ~3.58 MHz. With a tsc clocksource it gives
the minimum accepted resolution of 2 ns and with kvm-clock 10 ns.

As the final value of the precision, use the minimum value from the
measured or clock_getres() resolution and the original minimum time
needed to read the clock.
2025-10-09 11:10:51 +02:00
Miroslav Lichvar
8084961011 leapdb: fix compiler warning on NetBSD about isspace() 2025-10-09 10:44:31 +02:00
Miroslav Lichvar
120bf44989 test: fix socket unit test to use non-blocking accepted sockets
SCK_AcceptConnection() always returns a non-blocking socket. Clear the
O_NONBLOCK flag in the socket unit test, which relies on blocking, to
avoid failures.

Reported-by: Matthias Andree <matthias.andree@gmx.de>
2025-08-28 09:39:26 +02:00
Miroslav Lichvar
9e8541e3c4 sys_linux: improve error message for failed PHC open
If the specified PHC device cannot be opened directly, an attempt is
made to open it as a network interface. When that fails, the error
"Could not open PHC of iface" is misleading the user that it was handled
only as an interface. Change the message to "Could not open PHC (of)" to
better cover both possibilities. Also remove the errno as it's not set
in all code paths.
2025-08-27 14:05:31 +02:00
Miroslav Lichvar
e95d5a161d test: avoid using cmdport equal to ntpport in system tests
Make sure the two randomly generated port numbers used in system tests
are different to avoid failures.
2025-08-27 14:05:27 +02:00
Miroslav Lichvar
2c63dfee34 doc: update URL in test documentation 2025-08-26 12:34:22 +02:00
Miroslav Lichvar
42e6b5577a test: check credentials in nts_ke_session test
Make it more clear when the test fails because the credentials could not
be created.
2025-08-26 12:33:14 +02:00
Miroslav Lichvar
830c8bb18a util: switch create_dir() from chown() to lchown()
Use lchown(), the safer variant of chown() that does not follow
symlinks, when changing the ownership of a created directory (logdir,
dumpdir, ntsdumpdir, and the directory of bindcmdaddress) to the chrony
user.
2025-08-26 12:32:33 +02:00
Miroslav Lichvar
0289442998 client: fix sizeof in open_unix_socket()
Fix one of the sizeofs in open_unix_socket() to correctly specify
sock_dir2 instead of sock_dir1. They have the same size, but don't rely
on that.

Fixes: 90d808ed28 ("client: mitigate unsafe permissions change on chronyc socket")
2025-08-26 09:59:37 +02:00
Miroslav Lichvar
e9848c0176 doc: update NEWS 2025-08-14 15:55:15 +02:00
Miroslav Lichvar
2cfe969940 doc: update credits in README 2025-08-14 15:30:27 +02:00
Miroslav Lichvar
487cf3840f doc: update FAQ 2025-08-14 15:30:27 +02:00
Miroslav Lichvar
4886c776d5 update copyright years 2025-08-14 15:30:27 +02:00
Miroslav Lichvar
d3f3638b3d util: avoid compiler warning in UTI_IPSockAddrToString()
Don't print directly a buffer of the pool to another buffer of
the pool to avoid a -Wrestrict warning produced by a recent gcc version.
2025-08-14 15:30:27 +02:00
Miroslav Lichvar
6c5973741b configure: fix compiler warnings in system function checks
Avoid using (void *)1 as an output buffer to fix detection of supported
system functions due to -Werror and -Wstringop-overflow etc.
2025-08-14 14:25:38 +02:00
Miroslav Lichvar
51d161a028 refclock: rework update of reachability again
The recent rework of refclock reachability to better work with
driver-specific filtering (PHC driver dropping samples with unexpected
delay) introduced an issue that a PPS refclock is indicated as reachable
even when its "lock" refclock is permanently unreachable, or its samples
constistently fail in other sample checks, and no actual samples can be
accumulated. This breaks the new maxunreach option.

Rework the refclock code to provide samples from drivers together with
their quality level (all drivers except PHC provide samples with
constant quality of 1) and drop samples with quality 0 after passing
all checks, right before the actual accumulation in the median sample
filter. Increment the reachability counter only for samples that would
be accumulated.

This fixes the problem with refclocks indicated as reachable when their
samples would be dropped for other reasons than the PHC-specific delay
filter, and the maxunreach option can work as expected.

Fixes: b9b338a8df ("refclock: rework update of reachability")
2025-08-14 14:25:38 +02:00
Miroslav Lichvar
5535384878 hwclock: don't drop valid samples in HCL_ProcessReadings()
Modify the HCL_ProcessReadings() function to try to always provide
a valid sample. Instead of dropping a sample outside of the expected
delay, provide its assumed quality level as a small integer (relative to
already accumulated samples), and let the caller decide what quality is
acceptable.
2025-08-14 14:24:54 +02:00
Miroslav Lichvar
f78e4681ef refclock_phc: open device for writing with extpps option
In version 6.15 the Linux kernel started checking write access on the
PHC file descriptor in the PTP_PIN_SETFUNC and PTP_EXTTS_REQUEST ioctls.
chronyd opened the PHC device as readonly, which caused the PHC refclock
driver configured with the extpps option to fail with the
"Could not enable external PHC timestamping" error message.

To ensure compatibility with new kernel versions, add flags to the
SYS_Linux_OpenPHC() function and open the device with the O_RDWR flag
when the extpps option is enabled.
2025-08-07 14:43:37 +02:00
Miroslav Lichvar
b365edb48e tls: don't accept NULL ALPN name in TLS_CreateInstance()
The TLS_CreateInstance() function handles a NULL alpn_name, but the
other session functions would crash if it was NULL. Change the function
to not handle the NULL for consistency and avoid potential confusion.

Fixes: 3e32e7e694 ("tls: move gnutls code into tls_gnutls.c")
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
93a78c73ad tls: fix server log messages to have client IP address
Add an additional parameter to TLS_CreateInstance() to save the label of
the connection (server name on the client side and client IP
address:port on the server side) instead of the server name (which is
NULL on the server side) to fix the log messages.

Fixes: 3e32e7e694 ("tls: move gnutls code into tls_gnutls.c")
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
abc267a556 tls: don't call gnutls_deinit() after failed gnutls_init()
Don't assume gnutls_init() leaves the session pointer at NULL when it
returns with an error status. It might be a session that was already
allocated and then freed without resetting it to NULL after an error.

Fixes: 3e32e7e694 ("tls: move gnutls code into tls_gnutls.c")
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
9b183fe98f sources: add option to limit selection of unreachable sources
Add maxunreach option to NTP sources and refclocks to specify the
maximum number of polls that the source can stay selected for
synchronization when it is unreachable (i.e. no valid sample was
received in the last 8 polls).

It is an additional requirement to having at least one sample more
recent than the oldest sample of reachable sources.

The default value is 100000. Setting the option to 0 disables selection
of unreachable sources, which matches RFC 5905.
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
be7f5e8916 client: add support for dropping root privileges
To minimize the impact of potential attacks targeting chronyc started
under root (e.g. performed by a local chronyd process running without
root privileges, a remote chronyd process, or a MITM attacker on the
network), add support for changing the effective UID/GID in chronyc
after start.

The user can be specified by the -u option, similarly to chronyd. The
default chronyc user can be changed by the --with-chronyc-user
configure option. The default value of the default chronyc user is
"root", i.e. chronyc doesn't try to change the identity by default.

The default chronyc user does not follow the default chronyd user
set by the configure --with-user option to avoid errors on systems where
chronyc is not allowed to change its UID/GID (e.g. by a SELinux policy).
2025-08-07 10:18:31 +02:00
Miroslav Lichvar
5e2cd47ad1 test: fix system tests to change also tempcomp owner 2025-07-30 14:57:01 +02:00
Miroslav Lichvar
9eaf8bc521 socket: rename sun variable to fix compilation on illumos
"sun" is reserved on Solaris/illumos.

Fixes: 3dea7dd723 ("socket: rework setting of struct sockaddr_un")
2025-07-30 14:46:59 +02:00
Miroslav Lichvar
54010586aa socket: remove unused chmod() call
Drop the SCK_FLAG_ALL_PERMISSIONS support from the socket code.
chronyc is now calling chmod() on its socket itself in a hidden
directory to mitigate the unsafe operation.
2025-07-30 14:46:59 +02:00
Miroslav Lichvar
90d808ed28 client: mitigate unsafe permissions change on chronyc socket
When chronyc running under root binds its Unix domain socket, it needs
to change the socket permissions in order for chronyd running without
root privileges to be able to send a response to the socket.

There is a race condition between the bind() and chmod() calls. If an
attacker was able to execute arbitrary code in the chronyd process, it
might be able to wait for chronyc to be executed under root, replace the
socket with a symlink between the two calls, and cause the privileged
chronyc process to change permissions of something else, possibly
leading to a privilege escalation.

There doesn't seem to be a safe and portable way to change the socket
permissions directly. Changing the process umask could be problematic in
future with threads.

Hide the socket in two levels of subdirectories (the lower one having
a randomly generated name and not visible to the chronyd process) to
make the socket path unpredictable, and force the bind() or chmod() call
to fail if the visible upper directory is replaced.

Reported-by: Matthias Gerstner <mgerstner@suse.de>
2025-07-30 14:46:59 +02:00
Miroslav Lichvar
1d9e080749 util: warn if UTI_OpenFile() is stuck in a loop
When UTI_OpenFile() is removing an existing file to be replaced by a new
file, it could potentially get stuck in an infinite loop if something
was able to consistently win the race and create a new file before
chronyd.

Log a warning message after 100 failed attempts and repeat on each 10x
increase to make it more obvious to the admin, if it ever happens.

Reported-by: Eric Sesterhenn <eric.sesterhenn@x41-dsec.de>
2025-07-02 16:02:41 +02:00
Miroslav Lichvar
d30913e78c keys: remove misleading memset()
After (re)loading symmetric NTP keys from the key file, there is an
attempt to erase the strings from the stack by calling memset() on the
buffer. However, compilers are free (and have been shown to do) optimize
this call out.

Remove the memset() call to not pretend the stack cannot not contain any
sensitive information. There is no such attempt made for the server and
client NTS keys.

Reported-by: Eric Sesterhenn <eric.sesterhenn@x41-dsec.de>
2025-07-02 16:02:31 +02:00
Ahmad Fatoum
c5d3be8cc4 leapdb: fix ordered comparison against NULL pointer
fgets returns either a valid pointer with the same value as its first
argument or NULL on error or EOF.

GCC 12.2.0 -Wextra warns against relational comparison of the return
value:

  leapdb.c:127:38: warning: ordered comparison of pointer with integer zero [-Wextra]

For clarity, and because the C standard doesn't mandate that valid pointers
have to compare greater than the null pointer constant, replace the
relational expression with an equality expression
2025-07-02 14:49:21 +02:00
Anthony Brandon
3e32e7e694 tls: move gnutls code into tls_gnutls.c
Currently nts_ke_session.c directly calls into gnutls.
This patch moves the calls to gnutls into tls_gnutls.c with an API
defined in tls.h. This way it becomes possible to use different TLS
implementations in future patches.

Signed-off-by: Anthony Brandon <anthony@amarulasolutions.com>
2025-06-26 15:53:41 +02:00
Miroslav Lichvar
52cce3dea8 sys_linux: drop support for kernels before 2.6.39
Linux 2.6.39 was released in 2011.

Refuse to start if a kernel version before 2.6.39 is detected. Assume
the ADJ_SETOFFSET adjtimex mode is always supported. Its verification
briefly reset the timex maxerror value to 0, which possibly confused
applications checking the value at that moment.

Drop the unneeded workaround for slow frequency updates in versions
2.6.27-2.6.32.
2025-06-24 15:49:33 +02:00
Miroslav Lichvar
1bcbea9bd2 doc: update NEWS 2025-06-11 15:06:19 +02:00
Miroslav Lichvar
2ac581e04a update copyright years 2025-06-11 15:06:19 +02:00
Miroslav Lichvar
4a8da7e02d examples: improve chrony.conf examples
Add a note that three servers is the generally recommended minimum for
an NTP client to be able to detect a falseticker. Mention that the pool
directive uses four servers. Update the links to the pool join page and
list of public servers.
2025-06-11 15:06:15 +02:00
Miroslav Lichvar
e463fcab49 refclock_rtc: fix finalization with closed descriptor
If the RTC file descriptor was closed and removed after a read error,
don't try to close and remove it again in the driver finalization to
avoid an assertion failure on the negative descriptor.

Fixes: 4f22883f4e ("refclock: add new refclock for RTCs")
2025-06-11 13:39:17 +02:00
Miroslav Lichvar
df98fb4fc7 logging: don't close stderr in finalization
When logging to stderr, don't close it in finalization in case something
else still wanted to write to it. Leave it as it is together with stdin
and stdout.
2025-06-11 09:22:32 +02:00
Miroslav Lichvar
551bc266e4 test: extend 110-chronyc test 2025-06-09 12:08:57 +02:00
Miroslav Lichvar
c4234dd1f7 test: extend 009-sourceselection test 2025-06-09 12:08:51 +02:00
Miroslav Lichvar
fd50f3c80c test: include disabled cmdmon in 003-sanitizers 2025-06-05 13:33:00 +02:00
Miroslav Lichvar
9b9823b377 test: fix 015-ipv6 test to skip when IPv6 is disabled 2025-06-05 13:28:58 +02:00
Miroslav Lichvar
c900dff314 test: fix tests for disabled cmdmon 2025-06-03 15:42:05 +02:00
Miroslav Lichvar
2c6cbde058 test: add 149-sourcedir test 2025-06-03 13:28:46 +02:00
Miroslav Lichvar
cacd64bf4a conf: fix sourcedir reloading to not multiply sources
The sourcedir reload triggered by the chronyc "reload sources"
command incorrectly assumed that NSR_AddSourceByName() can return
only the NSR_Success status when a source is added. It ignored the
NSR_UnresolvedName status returned for a source whose name needs to
be resolved after the call (i.e. not specified with an IP address)
and added the source again, effectively multiplying it if the name
can be resolved to a different IP address.

Fix the code to check for the NSR_UnresolvedName status to correctly
determine whether the source was already added before and should not be
added again.

Reported-by: MichaelR <MichaelR42@runbox.com>
Fixes: 916ed70c4a ("conf: save source status in sourcedir reload")
2025-06-03 11:41:12 +02:00
Miroslav Lichvar
6c2ee89970 doc: mention RFC on interleaved modes
The specification of the interleaved modes is now published as
RFC 9769.
2025-05-27 10:56:27 +02:00
Miroslav Lichvar
68b2ffa97c test: make 007-cmdmon test more reliable 2025-05-27 10:56:27 +02:00
Miroslav Lichvar
f591133f4c test: add RTC test to 106-refclock 2025-05-27 10:56:27 +02:00
Miroslav Lichvar
48cc072c06 test: fix IPv6 test in 139-nts
Make sure an IPv6 address is actually used and don't forget to remove
the measurements log to not interfere with the subsequent test.
2025-05-27 10:56:11 +02:00
Miroslav Lichvar
8211978570 sched: don't define FD_SETSIZE
Don't make any assumptions about fd_set size if FD_SETSIZE is missing.
POSIX requires the macro to be defined.
2025-05-26 16:34:23 +02:00
Miroslav Lichvar
4b2b6bbf97 main: improve error message about failed notification
Mention the NOTIFY_SOCKET variable to make it more obvious what is
preventing chronyd from starting in case it's unexpectedly inherited in
a chroot etc.
2025-05-22 14:47:36 +02:00
Vincent Blut
551ad40a04 doc: fix typo in chronyc man page 2025-05-22 08:22:20 +02:00
Miroslav Lichvar
09cd057c23 doc: update NEWS 2025-05-21 12:57:55 +02:00
Miroslav Lichvar
363aa86861 doc: update README 2025-05-21 12:41:13 +02:00
Miroslav Lichvar
bda2ff77e8 getdate+nts+rtc: avoid some coverity false positives
Modify the code to avoid making the following calls incorrectly reported
as important findings by the coverity static analyzer:

- memset() of size 0 at the end of an array
- mktime() on a struct tm that has uninitialized tm_yday
2025-05-21 12:41:13 +02:00
Miroslav Lichvar
545bd59563 socket: drop messages from unterminated Unix paths
On some systems (e.g. FreeBSD) the source Unix domain socket path
provided by recvmsg() as msg_name is not always null-terminated even if
more space than required for sockaddr_un is provided due to the padding
in the sockaddr_all union, and the returned msg_namelen value does not
indicate it is missing the termination. If a cmdmon client bound its
socket to a maximum-length path (chronyc doesn't allow that), the path
would be overread when printing a debug message and trying to send a
response.

Drop messages from paths not shorter than sun_path to avoid working with
un-printf()able and/or unreachable addresses. The clients are expected
to not use the maximum-length paths.
2025-05-21 12:41:13 +02:00
Miroslav Lichvar
082af24114 local+reference: fix tracking offset after failed step
If a clock step enabled by the makestep directive or requested by the
makestep command fails, accumulate the missing step back to keep the
tracking offset valid.

This fixes time served by an instance configured with the makestep
directive and the -x option (the null driver cannot perform steps) at
the same time. It will still generate error log messages.
2025-05-21 12:41:13 +02:00
Miroslav Lichvar
577221295f doc: warn about makestep not working with -x option 2025-05-21 12:41:13 +02:00
Miroslav Lichvar
eb08c3b013 doc: specify maximum accepted value of ntsprocesses 2025-05-21 12:41:13 +02:00
Miroslav Lichvar
391882dc41 conf+cmdparse: parenthesize all macro arguments 2025-05-21 12:41:13 +02:00
Miroslav Lichvar
b32d85bc54 doc: improve description of server directive 2025-05-21 12:04:45 +02:00
Andreas Fenkart
0bf7e49148 examples: fix unset OPTIONS variable warning in systemd services
Modify chronyd.service to handle cases where OPTIONS is undefined,
which occurs when /etc/sysconfig/chronyd doesn't exist or doesn't set
the variable. This prevents the warning:

 "chronyd.service: Referenced but unset environment variable
  evaluates to an empty string: OPTIONS"
2025-05-12 11:31:42 +02:00
Miroslav Lichvar
f5fdfee150 configure: check for nettle_memeql_sec()
This fixes build with nettle versions before 3.3.
2025-05-12 11:21:06 +02:00
Miroslav Lichvar
c17f481a20 socket: fix compiler warning on macOS
Disable get_default_inet_domain() together with check_socket_flag() to
avoid a warning about unused function.

Reported-by: Bryan Christianson <bryan@whatroute.net>
2025-05-05 12:21:03 +02:00
Miroslav Lichvar
8140feb4fd test: fix 110-chronyc for disabled SW timestamping 2025-05-05 12:21:03 +02:00
Shachar Raindel
6e541afbe9 refclock_phc: support ethernet ifname as specifier
This commit allows the user to select a PHC refclock associated with
an Ethernet interface by specifying the interface name. This allows
the user to handle situations where multiple NICs are exposing PHC
devices (or non-NIC PHC device files exist in the system) in a more
streamline manner.
2025-05-05 12:19:17 +02:00
Miroslav Lichvar
81362fa201 test: add IPv6 simulation tests 2025-04-30 15:00:24 +02:00
Miroslav Lichvar
ec57de02c7 socket: open IPv6 socket by default if IPv4 is disabled
When no remote and local address is specified, and IPv4 is disabled by
the -6 option, open an IPv6 socket. This is used by the Linux-specific
timestamping configuration and socket option checking. It enables
operation on a system that has no support for IPv4 sockets.
2025-04-30 15:00:22 +02:00
Paul Donald
f7da309b83 doc: diverse fixes 2025-04-28 10:40:38 +02:00
Paul Donald
9b28b2f493 doc: un-split infinitives 2025-04-28 10:40:38 +02:00
Paul Donald
ebf19ca760 doc: fix should word order 2025-04-28 10:40:36 +02:00
Paul Donald
2d96077b9f doc: improve the usage of however
However at the start means "in whatever way"/"to whatever extent".
("However chrony is configured, it won't let you in without allow")
However incorrectly at the start usually means "But" was intended.
2025-04-28 10:31:19 +02:00
Paul Donald
2c2dd2d126 doc: improve description of logbanner 2025-04-28 10:31:19 +02:00
Miroslav Lichvar
f7daec0693 sys_timex: detect clock interference from other processes
After an ntp_adjtime()/adjtimex() call, check if the frequency, PLL time
constant and PLL status are as expected from the previous call. If they
changed, log a warning message to indicate that another NTP client might
be running on the system and interfering with the system clock.
2025-04-03 16:27:26 +02:00
Miroslav Lichvar
d115449e85 keys: compare MACs in constant time
Switch from memcmp() to the new constant-time function to compare the
received and expected authentication data generated with a symmetric key
(NTP MAC or AES CMAC).

While this doesn't seem to be strictly necessary with the current
code, it is a recommended practice to prevent timing attacks. If
memcmp() compared the MACs one byte at a time (a typical memcmp()
implementation works with wider integers for better performance) and
chronyd as an NTP client/server/peer was leaking the timing of the
comparison (e.g. in the monitoring protocol), an attacker might be able
for a given NTP request or response find in a sequence the individual
bytes of the MAC by observing differences in the timing over a large
number of attempts. However, this process would likely be so slow the
authenticated request or response would not be useful in a MITM attack
as the expected origin timestamp is changing with each poll.

Extend the keys unit test to compare the time the function takes to
compare two identical MACs and MACs differing in the first byte
(maximizing the timing difference). It should fail if the compiler's
optimizations figure out the function can return early. The test is not
included in the util unit test to avoid compile-time optimizations with
the function and its caller together. The test can be disabled by
setting NO_TIMING_TESTS environment variable if it turns out to be
unreliable.
2025-04-03 16:27:02 +02:00
Miroslav Lichvar
dab98fa8da util: add function for constant-time memory comparison
Add a function to check if two buffers of the same length contain the
same data, but do the comparison in a constant time with respect to the
returned value to avoid creating a timing side channel, i.e. the time
depends only on the buffer length, not on the content.

Use the gnutls_memcmp() or nettle_memeql_sec() functions if available,
otherwise use the same algorithm as nettle - bitwise ORing XORed data.
2025-04-03 16:05:04 +02:00
Miroslav Lichvar
dd8738119b logging: try to reopen message log on cyclelogs command
When the cyclelogs command is issued, check if the file specified by the
-l option is still in its place and if not try opening it again. If that
fails (e.g. due to chrony no longer having root privileges), keep the
old file handle to avoid losing log messages.
2025-03-20 16:34:35 +01:00
Miroslav Lichvar
75bbccf518 configure: make NTP and ASYNCDNS support nonoptional
Don't allow the NTP support and asynchronous name resolving to be
disabled. pthreads are now a hard requirement.

NTP is the primary task of chrony. This functionality doesn't seem to be
commonly disabled (allowing only refclocks and manual input).

This removes rarely (if ever) used code and simplifies testing.
2025-03-20 16:34:35 +01:00
Miroslav Lichvar
1b24a66b3c conf: improve some error messages 2025-03-20 16:34:33 +01:00
Miroslav Lichvar
8fd386f287 conf+cmdparse: check sanity of configured integer values
Verify that integer values specified in the configuration are sane:
interval log2 values are between -32 and 32, ports between 0 and 65535,
stratum between 0 and 16, values that should not be negative are not
negative, numbers that specify large intervals in seconds fit in the
32-bit integer, numbers don't have non-digit characters, etc.
2025-03-20 16:04:09 +01:00
Miroslav Lichvar
e694ae769a cmdparse: add status for server and local command
Add an enum to describe the error in the parsed directive: missing
argument, invalid option, or invalid value.

Update the error messages in conf.c and client.c.
2025-03-20 15:57:55 +01:00
Miroslav Lichvar
2b127b2e93 conf: switch other_parse_error() to variable arguments 2025-03-20 15:55:25 +01:00
Miroslav Lichvar
2d3c89e1a3 conf: change parse_null() to accept pointer for consistency 2025-03-20 15:55:25 +01:00
Miroslav Lichvar
b5d1ae147d conf: don't return unused values from parse functions
No need to return success. These functions terminate the process on
errors.
2025-03-20 15:55:15 +01:00
Miroslav Lichvar
bf964673cb reference: add wait options for local reference activation
Add "waitunsynced" option to specify how long chronyd needs to wait
before it can activate the local reference when the clock is not
synchronized to give the configured sources a chance to synchronize the
local clock after start. The default is 300 seconds when the orphan
option is enabled (same as the ntpd's default orphanwait), 0 otherwise.

Add "waitsynced" option to specify how long it should wait when the
clock is synchronized. It is an additional requirement to the distance
and activate options.
2025-03-13 16:30:27 +01:00
Miroslav Lichvar
c66c774e26 sources: increase severity of can't-synchronise log messages
Switch the info-level "Can't synchronise" selection messages to
warnings.
2025-03-13 16:25:54 +01:00
Miroslav Lichvar
274c1767f2 sources: improve no-selectable-sources log message
Include the number of unreachable sources in the "Can't synchronise: no
selectable sources" log message to provide a hint whether it might be a
networking issue.
2025-03-13 16:25:54 +01:00
Miroslav Lichvar
c27a298d9c sources: switch unselect_selected_source() to variable arguments
Switch the function to the full printf style, which will be needed to
log an integer.
2025-03-13 16:25:42 +01:00
Miroslav Lichvar
072ec20e2c sources: delay maxjitter/maxdistance warning messages
Avoid logging the new warning messages about exceeded maxjitter or
maxdistance when only a small number of samples is collected after the
source becomes reachable and the values are unstable. Log the messages
only when a replacement attempt is made.
2025-03-13 16:16:28 +01:00
Miroslav Lichvar
711c7c0c8a rewrite some assertions for better readability
Some assertions are written as "if (x) assert(0)" to avoid having
the text of a long argument compiled in the binary. Rewrite them
to use a new BRIEF_ASSERT macro to make the condition easier to read in
its non-negated form and make it easier to turn it back to the full-text
assert if needed.
2025-03-06 15:22:36 +01:00
Miroslav Lichvar
454ce62672 sources: improve no majority log message
Add the number of sources that form an agreement (overlapping
intervals), if at least two agree with each other, and number of
reachable sources to the "Can't synchronize: no majority" log message to
better explain why synchronization is failing and hint that adding more
sources might help.
2025-03-06 15:22:36 +01:00
Miroslav Lichvar
ab850d41f4 sources: warn about sources exceeding maxdistance or maxjitter
Log a warning message if a source is rejected in the source selecting
due to exceeding the maxdistance or maxjitter limit to make it more
obvious when synchronization is failing for this reason. Delay the
message until the reachability register is full (8 updates), or a
replacement of the source is attempted.
2025-03-06 11:44:36 +01:00
Miroslav Lichvar
744768306b sources: refactor logging of source-specific selection status
Move logging of falsetickers and system peers to the mark_source()
function. Use an array to flag already logged messages in preparation
for logging of other status. Support variable number of arguments in the
logging functions.
2025-03-06 11:41:40 +01:00
Miroslav Lichvar
26d4f0c401 privops: mark res_fatal() for printf format compiler check 2025-03-05 16:39:13 +01:00
Miroslav Lichvar
717db2cfdd doc: improve description of refresh directive 2025-03-05 16:38:56 +01:00
Miroslav Lichvar
55898e9b07 client: fix memory leak of empty readline() string 2025-02-12 15:41:10 +01:00
Miroslav Lichvar
f7bb283536 doc: mention localhost exception in cmdallow description 2025-02-12 15:41:10 +01:00
Miroslav Lichvar
1967fbf1f2 cmdmon: make open commands configurable
Replace the hardcoded list of open commands (accessible over UDP),
with a list that can be configured with a new "opencommands" directive.
The default matches the original list. All read-only commands except
accheck and cmdaccheck can be enabled. The naming follows the chronyc
naming. Enable the N_SOURCES request only when needed.

This makes it possible to have a full monitoring access without access
to the Unix domain socket. It also allows restricting the monitoring
access to a smaller number of commands if some commands from the default
list are not needed.

Mention in the man page that the protocol of the non-default commands is
not consider stable and the information they provide may have security
implications.
2025-02-12 15:40:13 +01:00
Miroslav Lichvar
51da7a0694 cmdmon: refactor command authorization checks
Try to simplify the code and make it more robust to potential bugs.

Instead of maintaing a table mapping all commands to open/auth
permissions, use a short list of open commands. Split the processing
of the commands into two groups, read-write commands and read-only
(monitoring) commands, where the first group is processed only with full
access. Check both the socket descriptor and address type before giving
full access. While moving the code, reorder the commands alphabetically.
2025-02-12 15:10:56 +01:00
Miroslav Lichvar
9ba6e7655c cmdmon: drop handling of NULL and LOGON requests
Handle the NULL and LOGON requests as unknown (invalid) instead of
returning the success and failed status respectively. They have
been unused for very long time now.
2025-02-12 14:52:19 +01:00
Miroslav Lichvar
3dea7dd723 socket: rework setting of struct sockaddr_un
Add a function to fill the Unix sockaddr for binding, connecting and
sending to avoid code duplication. Use memcpy() instead of snprintf()
and provide the minimum length of the address containing the terminating
null byte. This makes it easier to support abstract sockets if needed in
future (e.g. for systemd support).
2025-02-05 16:10:31 +01:00
Miroslav Lichvar
cb1118456b main: add support for systemd notification
On Linux, if the NOTIFY_SOCKET variable is set, send a "READY=1"
and "STOPPING=1" message to the Unix domain socket after initialization
and before finalization respectively. This is used with the systemd
"notify" service type as documented in the sd_notity(3) man page. It's
a recommended alternative to the "forking" service type, which does not
need the PID file to determine the main process.

Support pathname Unix sockets only. Abstract sockets don't seem to be
used by systemd for notifications since version 212.

Switch the example services to the notify type, but keep the PID
file. It's still useful to prevent start of other chronyd instances.
systemd doesn't seem to care about the content of the file and should
just remove it in case chronyd didn't terminate cleanly.

Suggested-by: Luca Boccassi <bluca@debian.org>
2025-02-05 16:10:04 +01:00
Miroslav Lichvar
8ee725ff18 refclock: drop filter length adjustment for polling drivers
In the refclock initialization, if the driver provides a poll()
function and 2^(poll-dpoll) is smaller than the configured length of the
median filter (64 by default), the filter is shortened to 2^(poll-dpoll)
samples, assuming the driver provides samples only in the poll()
function and at most one per call, to avoid wasting memory and before
commit 12237bf283 ("refclock: stop requiring 4 samples in median
filter") also simplify configuration (for polling drivers only)

But this assumption is not always correct. The PHC driver can read
external PPS timestamps independently from the driver polling and the
RTC driver can timestamp interrupts. If the dpoll was too large to cover
the sample rate, some samples would be lost.

Drop the adjustment of the filter length to avoid this unexpected impact
on filtering and make it work as documented.
2024-12-11 11:45:13 +01:00
Bryan Christianson
a90a7cf51c refclock: fix build on non-Linux systems
Fixes: 5fd71e27831f ("refclock: add new refclock for RTCs")
2024-12-10 10:14:43 +01:00
Uwe Kleine-König
4f22883f4e refclock: add new refclock for RTCs
This refclock uses an RTC as reference source. If the RTC doesn't
support reporting an update event this source is quite coarse as it
usually needs a slow bus access to be read and has a precision of only
one second. If reporting an update event is available, the time is read
just after such an event which improves precision.

Depending on hardware capabilities you might want to combine it with a
PPS reference clock sourced from the same chip.

Note that you can enable UIE emulation in the Linux kernel to make a RTC
without interrupt support look like one with irqs in return for some
system and bus overhead.

Co-authored-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
2024-12-04 15:58:10 +01:00
Ahmad Fatoum
65be9d9a02 rtc: optionally return raw time from RTC_Linux_ReadTime*
For use with RCL_AddSample in the incoming RTC reference clock driver,
we'll want access not to the cooked timestamps, but to the raw ones.

Otherwise, the core reference clock code complains:

  (valid_sample_time) RTC0 refclock sample time 1721242673.092211891
    not valid age=-3.092007

Support both use cases by have the RTC_Linux_ReadTime_* functions take
two nullable pointers, one for cooked and the other for raw time.
2024-12-04 15:24:52 +01:00
Ahmad Fatoum
a1191f8392 rtc: factor out RTC_Linux_ReadTimeNow
We have code to read RTC time and handle the error associated with
having no UIE interrupt, which we currently use as part of maintaining
the correction file.

In a later commit, we will need the same functionality for using the RTC
as reference clock, so export the function and give it a descriptive
name appropriate for a globally visible function.
2024-12-04 15:24:48 +01:00
Ahmad Fatoum
924199ef69 rtc: factor out RTC_Linux_ReadTimeAfterInterrupt
We have code to read time after an RTC's UIE interrupt, which we
currently use as part of maintaining the correction file.

In a later commit, we will need the same functionality for using the RTC
as reference clock, so export the function and give it a descriptive
name appropriate for a globally visible function.
2024-12-04 14:54:04 +01:00
Ahmad Fatoum
8028984f34 rtc: export RTC_Linux_{SwitchInterrupt,CheckInterrupt}
We have code to enable and disable the RTC's UIE interrupt, as well as
check whether it occurred and skip the first interrupt as it may be
bogus.
This is currently used as part of maintaining the correction file.

In a later commit, we will need the same functionality for using the RTC
as reference clock, so export the functions and give them descriptive
names appropriate for globally visible functions.
2024-12-04 14:51:25 +01:00
Ahmad Fatoum
68301238a0 rtc: pass info whether RTC is on UTC as a function parameter
rtc_from_t() and t_from_rtc() call either gmtime or localtime depending
on the value of a global rtc_on_utc variable.

This will not be appropriate anymore when we start exporting functions
that call rtc_from_t() and t_from_rtc() for use outside of rtc_linux.c
as the rtc_on_utc variable may not have been initialized yet or at all.

Therefore make whether the RTC is on UTC a function parameter of these
functions, so the value can be propagated from the callers.
2024-12-04 12:09:03 +01:00
Ahmad Fatoum
13db3d6902 rtc: let t_from_rtc/rtc_from_t operate on struct rtc_time directly
Both functions operate on struct tm even though the struct tm is only
used as intermediary format from struct rtc_time.

In the case of rtc_from_t, the code to convert from struct tm to struct
rtc is even duplicated.

Let's simplify code a bit by moving the struct translation code into
these conversion functions.
2024-12-04 12:09:03 +01:00
Miroslav Lichvar
8c24086c6d test: pass make options in features compilation test 2024-12-04 12:08:57 +01:00
Miroslav Lichvar
e30d5a6e6f test: extend socket unit test 2024-12-04 12:06:29 +01:00
Miroslav Lichvar
a0d34eb372 test: add valgrind support to system tests 2024-11-28 16:28:58 +01:00
Miroslav Lichvar
7196943f11 nts: close socket in helper process on exit
Close the socket used for receiving helper requests before exit to avoid
another valgrind error.
2024-11-28 16:09:19 +01:00
Miroslav Lichvar
26fef1751a main: close pipe in grandparent process
Close the other end of the pipe in the grandparent process before exit
to avoid valgrind error.

Also, in the daemon process avoid closing the pipe for second time in
the 0-1024 close() loop to avoid another error.
2024-11-28 14:35:58 +01:00
Miroslav Lichvar
d71e22594e client: close /dev/urandom on exit 2024-11-28 14:35:01 +01:00
Miroslav Lichvar
d7a578fed2 test: enable valgrind fd tracking 2024-11-28 11:17:51 +01:00
Miroslav Lichvar
014a67b29e test: update 106-refclock
With the latest clknetsim all PHC readings should be valid (no
timestamps from future).

Also don't forget to remove refclock.log between tests.
2024-11-28 10:02:58 +01:00
Miroslav Lichvar
d22c8fbcb2 quantiles: add parameter to limit negative step
Add a new parameter to limit the negative value of the step state
variable. It's set as a maximum delay in number of updates before the
actual step applied to the quantile estimate starts growing from the
minimum step when the input value is consistently larger or smaller than
the estimate.

This prevents the algorithm from effectively becoming the slower 1U
variant if the quantile estimate is stable most of the time.

Set it to 100 updates for the NTP delay and 1000 updates for the hwclock
delay. An option could be added later to make it configurable.
2024-11-21 16:00:23 +01:00
Miroslav Lichvar
2da4e3ce53 quantiles: force step update with stable input values
The algorithm was designed for estimating quantiles in streams of
integer values. When the estimate is equal to the input value, the
step state variable does not change. This causes problems for the
floating-point adaptation used for measurents of delay in chrony.

One problem is numerical instability due to the strict comparison of
the input value and the current estimate.

Another problem is with signals that are so stable that the nanosecond
resolution of the system functions becomes the limitation. There is a
large difference in the value of the step state variable, which
determines how quickly the estimate will adapt to a new distribution,
between signals that are constant in the nanosecond resolution and
signals that can move in two nanoseconds.

Change the estimate update to never consider the input value equal to
the current estimate and don't set the estimate exactly to the input
value. Keep it off by a quarter of the minimum step to force jumping
around the input value if it's constant and decreasing the step variable
to negative values. Also fix the initial adjustment to step at least by
the minimum step (the original algorithm is described with ceil(), not
fabs()).
2024-11-21 15:59:56 +01:00
Miroslav Lichvar
c92358e041 ntp+hwclock: add margin to estimated delay quantiles
Extend the interval of accepted delays by half of the quantile minimum
step in both directions to make room for floating-point errors in the
quantile calculation and an error that will be intentionally added in
the next commit.
2024-11-21 15:59:47 +01:00
Miroslav Lichvar
77962bb527 quantiles: add functions to get max k and min step 2024-11-21 09:05:58 +01:00
Miroslav Lichvar
65b5f111d2 quantiles: fix assertion for requested k 2024-11-18 15:51:42 +01:00
Miroslav Lichvar
9856bf2d5f test: improve nts_ntp_auth test 2024-11-14 13:43:22 +01:00
Miroslav Lichvar
2205eae2a1 test: add 014-intermittent test 2024-11-14 13:43:18 +01:00
Miroslav Lichvar
6f381fa78b test: indicate failed sync stats 2024-11-14 13:43:18 +01:00
Miroslav Lichvar
bb8050d884 test: make system test users configurable 2024-11-14 13:43:18 +01:00
Miroslav Lichvar
c85ec4ff0f siv: drop internal implementation
Nettle (>=3.6) and GnuTLS (>=3.6.14) with the AES-SIV-CMAC support
required for NTS are now widely available in operating systems. Drop
the internal Nettle-based implementation.
2024-11-14 13:43:18 +01:00
Miroslav Lichvar
d9d97f76d7 reference: make driftfile update interval configurable
Add interval option to the driftfile directive to specify the minimum
interval between updates of the recorded frequency offset and skew.
2024-11-14 13:43:18 +01:00
lvgenggeng
837323d687 clientlog: simplify code 2024-11-07 12:10:04 +01:00
Miroslav Lichvar
12237bf283 refclock: stop requiring 4 samples in median filter
Reduce the minimum number of samples required by the filter from
min(4, length) to 1.

This makes the filtering less confusing. The sample lifetime is limited
to one poll and the default filtering of the SOCK refclock (where the
maximum number of samples per poll is unknown) is identical to the other
refclocks.

A concern with potential variability in number of samples per poll below
4 is switching between different calculations of dispersion in
combine_selected_samples() in samplefilt.c.

The 106-refclock test shows how the order of refclocks in the config can
impact the first filtered sample and selection. If the PPS refclock
follows SHM, a single low-quality PPS sample is accepted in the same
poll where SHM is selected and the initial clock correction started,
which causes larger skew later and delays the first selection of the PPS
refclock.
2024-11-05 16:03:40 +01:00
Miroslav Lichvar
b9b338a8df refclock: rework update of reachability
Update the reachability register of a refclock source by 1 if a valid
measurement is received by the drivers between source polls, and not
only when it is accumulated to sourcestats, similarly to how
reachability works with NTP sources.

This avoids drops in the reported reachability when a PHC refclock is
dropping samples due to significant changes in the measured delay (e.g.
due to high PCIe load), or a PPS refclock dropping samples due to failed
lock.
2024-11-05 15:51:43 +01:00
Vincent Blut
a5d73f692f doc: fix typo in chrony.conf man page 2024-10-09 09:05:41 +02:00
116 changed files with 4137 additions and 2504 deletions

View File

@@ -37,7 +37,9 @@ GETDATE_CFLAGS = @GETDATE_CFLAGS@
EXTRA_OBJS = @EXTRA_OBJS@
OBJS = array.o cmdparse.o conf.o leapdb.o local.o logging.o main.o memory.o quantiles.o \
OBJS = addrfilt.o array.o clientlog.o cmdparse.o conf.o keys.o leapdb.o \
local.o logging.o main.o memory.o nameserv.o nameserv_async.o \
ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.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)

45
NEWS
View File

@@ -1,3 +1,48 @@
New in version 4.8
==================
Enhancements
------------
* Add maxunreach option to limit selection of unreachable sources
* Add -u option to chronyc to drop root privileges (default chronyc
user is set by configure script)
Bug fixes
---------
* Hide chronyc socket to mitigate unsafe permissions change
* Fix refclock extpps option to work on Linux >= 6.15
* Validate refclock samples for reachability updates
New in version 4.7
==================
Enhancements
------------
* Add opencommands directive to select remote monitoring commands
* Add interval option to driftfile directive
* Add waitsynced and waitunsynced options to local directive
* Add sanity checks for integer values in configuration
* Add support for systemd Type=notify service
* Add RTC refclock driver
* Allow PHC refclock to be specified with network interface name
* Don't require multiple refclock samples per poll to simplify
filter configuration
* Keep refclock reachable when dropping samples with large delay
* Improve quantile-based filtering to adapt faster to larger delay
* Improve logging of selection failures
* Detect clock interference from other processes
* Try to reopen message log (-l option) on cyclelogs command
Bug fixes
---------
* Fix sourcedir reloading to not multiply sources
* Fix tracking offset after failed clock step
Removed features
----------------
* Drop support for NTS with Nettle < 3.6 and GnuTLS < 3.6.14
* Drop support for building without POSIX threads
New in version 4.6.1
====================

5
README
View File

@@ -78,17 +78,21 @@ Vincent Blut <vincent.debian@free.fr>
Luca Boccassi <bluca@debian.org>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Anthony Brandon <anthony@amarulasolutions.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr>
Paul Donald <newtwen+gitlab@gmail.com>
Dan Drown <dan-ntp@drown.org>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com>
Ahmad Fatoum <a.fatoum@pengutronix.de>
Andreas Fenkart <extern-afe@mission-embedded.com>
Stefan R. Filipek <srfilipek@gmail.com>
Andy Fiddaman <illumos@fiddaman.net>
Mike Fleetwood <mike@rockover.demon.co.uk>
@@ -121,6 +125,7 @@ Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
Shachar Raindel <shacharr@google.com>
Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il>
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>

19
candm.h
View File

@@ -37,7 +37,6 @@
#define DEFAULT_CANDM_PORT 323
/* Request codes */
#define REQ_NULL 0
#define REQ_ONLINE 1
#define REQ_OFFLINE 2
#define REQ_BURST 3
@@ -223,11 +222,6 @@ typedef struct {
int32_t EOR;
} REQ_Modify_Makestep;
typedef struct {
Timespec ts;
int32_t EOR;
} REQ_Logon;
typedef struct {
Timespec ts;
int32_t EOR;
@@ -239,7 +233,8 @@ typedef struct {
Float distance;
int32_t orphan;
Float activate;
uint32_t reserved[2];
Float wait_synced;
Float wait_unsynced;
int32_t EOR;
} REQ_Local;
@@ -311,7 +306,7 @@ typedef struct {
int32_t filter_length;
uint32_t cert_set;
Float max_delay_quant;
uint32_t reserved[1];
int32_t max_unreach;
int32_t EOR;
} REQ_NTP_Source;
@@ -487,7 +482,6 @@ typedef struct {
REQ_Modify_Polltarget modify_polltarget;
REQ_Modify_Maxupdateskew modify_maxupdateskew;
REQ_Modify_Makestep modify_makestep;
REQ_Logon logon;
REQ_Settime settime;
REQ_Local local;
REQ_Manual manual;
@@ -517,13 +511,6 @@ typedef struct {
} CMD_Request;
/* ================================================== */
/* Authority codes for command types */
#define PERMIT_OPEN 0
#define PERMIT_LOCAL 1
#define PERMIT_AUTH 2
/* ================================================== */
/* Reply codes */

215
client.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2024
* Copyright (C) Miroslav Lichvar 2009-2025
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -47,6 +47,8 @@
#include <editline/readline.h>
#endif
#define MAX_UNIX_SOCKET_LENGTH (sizeof ((struct sockaddr_un *)NULL)->sun_path)
/* ================================================== */
struct Address {
@@ -61,6 +63,9 @@ static ARR_Instance server_addresses;
static int sock_fd = -1;
static char sock_dir1[MAX_UNIX_SOCKET_LENGTH];
static char sock_dir2[MAX_UNIX_SOCKET_LENGTH];
static volatile int quit = 0;
static int on_terminal = 0;
@@ -125,12 +130,11 @@ read_line(void)
strncpy(line, cmd, sizeof(line) - 1);
line[sizeof(line) - 1] = '\0';
add_history(cmd);
/* free the buffer allocated by readline */
Free(cmd);
} else {
/* simulate the user has entered an empty line */
*line = '\0';
}
Free(cmd);
return( line );
#else
printf("%s", prompt);
@@ -208,35 +212,139 @@ free_addresses(ARR_Instance addresses)
ARR_DestroyInstance(addresses);
}
/* ================================================== */
/* Bind a Unix domain socket and connect it to the server chronyd socket.
Access to the sockets is restricted by the permissions and ownership
of the directory in which they are bound (by default /var/run/chrony).
The location of the chronyc socket follows the location of the chronyd
socket. Due to the possibility of chronyc running under a different
user, the permissions of the chronyc socket need to be loosened on
most systems (illumos is an exception) to allow chronyd to send a
response to the chronyc socket.
Unfortunately, there does not seem to be a safe and portable way to
do that directly (e.g. a compromised chronyd process could replace it
with a symlink and cause a privileged chronyc process to change the
permissions of something else).
The workaround is to bind the socket in a randomly named subdirectory
hidden in another subdirectory to avoid matching an existing path and
force the bind()/chmod() call to fail if the visible upper directory
is replaced.
Note that the socket cannot be moved once bound, because the source
address that chronyd will see would not match the actual path and the
responses would not reach chronyc. */
static int
open_unix_socket(char *server_path)
{
char *sock_dir0, sock_path[MAX_UNIX_SOCKET_LENGTH], rand_dir[16 + 1];
int i, s, dir_fd1 = -1;
struct stat st;
/* Check if the server socket is accessible */
s = SCK_OpenUnixDatagramSocket(server_path, NULL, 0);
if (s < 0)
return -1;
SCK_CloseSocket(s);
/* Generate the random hidden component of the socket path */
for (i = 0; i + 1 < sizeof (rand_dir); i++) {
do
UTI_GetRandomBytesUrandom(&rand_dir[i], sizeof (rand_dir[i]));
while (!isalnum((unsigned char)rand_dir[i]));
}
rand_dir[i] = '\0';
/* Format the paths of the subdirectories and socket:
sock_dir0 = dirname(server_path)
sock_dir1 = sock_dir0/chronyc.$PID
sock_dir2 = sock_dir0/chronyc.$PID/$RANDOM
sock_path = sock_dir0/chronyc.$PID/$RANDOM/sock */
sock_dir0 = UTI_PathToDir(server_path);
if (snprintf(sock_dir1, sizeof (sock_dir1),
"%s/chronyc.%d", sock_dir0, (int)getpid()) >= sizeof (sock_dir1) ||
snprintf(sock_dir2, sizeof (sock_dir2),
"%s/%s", sock_dir1, rand_dir) >= sizeof (sock_dir2) ||
snprintf(sock_path, sizeof (sock_path),
"%s/sock", sock_dir2) >= sizeof (sock_path)) {
LOG(LOGS_ERR, "Server socket path %s is too long", server_path);
Free(sock_dir0);
goto error1;
}
Free(sock_dir0);
if (mkdir(sock_dir1, 0711) < 0 && errno != EEXIST) {
LOG(LOGS_ERR, "Could not create %s : %s", sock_dir1, strerror(errno));
goto error1;
}
dir_fd1 = open(sock_dir1, O_RDONLY | O_NOFOLLOW);
if (dir_fd1 < 0 || fstat(dir_fd1, &st) < 0) {
LOG(LOGS_ERR, "Could not open/stat %s : %s", sock_dir1, strerror(errno));
goto error2;
}
if (!S_ISDIR(st.st_mode) || (st.st_mode & 0777 & ~0711) != 0 || st.st_uid != geteuid()) {
LOG(LOGS_ERR, "Unexpected mode/owner of %s", sock_dir1);
goto error2;
}
if (mkdirat(dir_fd1, rand_dir, 0711) < 0) {
LOG(LOGS_ERR, "Could not create %s : %s", sock_dir2, strerror(errno));
goto error2;
}
s = SCK_OpenUnixDatagramSocket(server_path, sock_path, 0);
if (s < 0) {
LOG(LOGS_ERR, "Could not bind/connect client Unix socket");
goto error3;
}
if (chmod(sock_path, 0666) < 0 ||
chmod(sock_dir2, 0711) < 0 ||
fchmod(dir_fd1, 0711) < 0) {
LOG(LOGS_ERR, "Could not change socket or directory permissions : %s", strerror(errno));
SCK_RemoveSocket(s);
SCK_CloseSocket(s);
goto error3;
}
close(dir_fd1);
return s;
error3:
rmdir(sock_dir2);
error2:
rmdir(sock_dir1);
error1:
if (dir_fd1 >= 0)
close(dir_fd1);
sock_dir1[0] = '\0';
sock_dir2[0] = '\0';
return -1;
}
/* ================================================== */
/* Initialise the socket used to talk to the daemon */
static int
open_socket(struct Address *addr)
{
char *dir, *local_addr;
size_t local_addr_len;
switch (addr->type) {
case SCK_ADDR_IP:
sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
break;
case SCK_ADDR_UNIX:
/* Construct path of our socket. Use the same directory as the server
socket and include our process ID to allow multiple chronyc instances
running at the same time. */
dir = UTI_PathToDir(addr->addr.path);
local_addr_len = strlen(dir) + 50;
local_addr = Malloc(local_addr_len);
snprintf(local_addr, local_addr_len, "%s/chronyc.%d.sock", dir, (int)getpid());
sock_fd = SCK_OpenUnixDatagramSocket(addr->addr.path, local_addr,
SCK_FLAG_ALL_PERMISSIONS);
Free(dir);
Free(local_addr);
sock_fd = open_unix_socket(addr->addr.path);
break;
default:
assert(0);
@@ -258,6 +366,14 @@ close_io(void)
SCK_RemoveSocket(sock_fd);
SCK_CloseSocket(sock_fd);
if (sock_dir2[0] != '\0')
rmdir(sock_dir2);
if (sock_dir1[0] != '\0')
rmdir(sock_dir1);
sock_dir2[0] = '\0';
sock_dir1[0] = '\0';
sock_fd = -1;
}
@@ -754,12 +870,13 @@ process_cmd_burst(CMD_Request *msg, char *line)
static int
process_cmd_local(CMD_Request *msg, char *line)
{
double distance = 0.0, activate = 0.0, wait_synced = 0.0, wait_unsynced = 0.0;
int on_off, stratum = 0, orphan = 0;
double distance = 0.0, activate = 0.0;
if (!strcmp(line, "off")) {
on_off = 0;
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate)) {
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate,
&wait_synced, &wait_unsynced) == CPS_Success) {
on_off = 1;
} else {
LOG(LOGS_ERR, "Invalid syntax for local command");
@@ -772,7 +889,8 @@ process_cmd_local(CMD_Request *msg, char *line)
msg->data.local.distance = UTI_FloatHostToNetwork(distance);
msg->data.local.orphan = htonl(orphan);
msg->data.local.activate = UTI_FloatHostToNetwork(activate);
memset(msg->data.local.reserved, 0, sizeof (msg->data.local.reserved));
msg->data.local.wait_synced = UTI_FloatHostToNetwork(wait_synced);
msg->data.local.wait_unsynced = UTI_FloatHostToNetwork(wait_unsynced);
return 1;
}
@@ -906,8 +1024,9 @@ static int
process_cmd_add_source(CMD_Request *msg, char *line)
{
CPS_NTP_Source data;
CPS_Status status;
IPAddr ip_addr;
int result = 0, status, type;
int result = 0, type;
const char *opt_name, *word;
msg->command = htons(REQ_ADD_SOURCE);
@@ -928,10 +1047,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
status = CPS_ParseNTPSourceAdd(line, &data);
switch (status) {
case 0:
LOG(LOGS_ERR, "Invalid syntax for add command");
break;
default:
case CPS_Success:
/* Verify that the address is resolvable (chronyc and chronyd are
assumed to be running on the same host) */
if (strlen(data.name) >= sizeof (msg->data.ntp_source.name) ||
@@ -947,8 +1063,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
}
msg->data.ntp_source.type = htonl(type);
if (strlen(data.name) >= sizeof (msg->data.ntp_source.name))
assert(0);
BRIEF_ASSERT(strlen(data.name) < sizeof (msg->data.ntp_source.name));
strncpy((char *)msg->data.ntp_source.name, data.name,
sizeof (msg->data.ntp_source.name));
msg->data.ntp_source.port = htonl(data.port);
@@ -989,10 +1104,19 @@ process_cmd_add_source(CMD_Request *msg, char *line)
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));
msg->data.ntp_source.max_unreach = htonl(data.params.max_unreach);
result = 1;
break;
case CPS_InvalidOption:
LOG(LOGS_ERR, "Invalid %s add command", "option in");
break;
case CPS_InvalidValue:
LOG(LOGS_ERR, "Invalid %s add command", "value in");
break;
default:
LOG(LOGS_ERR, "Invalid %s add command", "syntax for");
break;
}
@@ -2452,10 +2576,10 @@ process_cmd_selectdata(char *line)
n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) {
printf( " . State: N - noselect, s - unsynchronised, M - missing samples,\n");
printf( " . State: N - noselect, s - unsynchronised, M - missing samples, r - stratum\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( "| 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");
@@ -3440,7 +3564,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2024 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2025 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",
@@ -3466,9 +3590,11 @@ print_help(const char *progname)
" -m\t\tAccept multiple commands\n"
" -h HOST\tSpecify server (%s)\n"
" -p PORT\tSpecify UDP port (%d)\n"
" -u USER\tSpecify user (%s)\n"
" -v, --version\tPrint version and exit\n"
" --help\tPrint usage and exit\n",
progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1", DEFAULT_CANDM_PORT);
progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1",
DEFAULT_CANDM_PORT, DEFAULT_CHRONYC_USER);
}
/* ================================================== */
@@ -3485,10 +3611,11 @@ int
main(int argc, char **argv)
{
char *line;
const char *hostnames = NULL, *user = DEFAULT_CHRONYC_USER;
const char *progname = argv[0];
const char *hostnames = NULL;
int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
int port = DEFAULT_CANDM_PORT;
struct passwd *pw;
/* Parse long command-line options */
for (optind = 1; optind < argc; optind++) {
@@ -3504,7 +3631,7 @@ main(int argc, char **argv)
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:v")) != -1) {
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:u:v")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -3540,6 +3667,9 @@ main(int argc, char **argv)
case 'p':
port = atoi(optarg);
break;
case 'u':
user = optarg;
break;
case 'v':
print_version();
return 0;
@@ -3549,6 +3679,14 @@ main(int argc, char **argv)
}
}
/* Drop root privileges if configured to do so */
if (strcmp(user, "root") != 0 && geteuid() == 0) {
pw = getpwnam(user);
if (!pw)
LOG_FATAL("Could not get user/group ID of %s", user);
UTI_DropRoot(pw->pw_uid, pw->pw_gid);
}
if (isatty(0) && isatty(1) && isatty(2)) {
on_terminal = 1;
}
@@ -3588,6 +3726,7 @@ main(int argc, char **argv)
close_io();
free_addresses(server_addresses);
SCK_Finalise();
UTI_ResetGetRandomFunctions();
return !ret;
}

View File

@@ -265,16 +265,13 @@ get_record(IPAddr *ip)
}
record->ip_addr = *ip;
for (i = 0; i < MAX_SERVICES; i++)
for (i = 0; i < MAX_SERVICES; i++) {
record->last_hit[i] = INVALID_TS;
for (i = 0; i < MAX_SERVICES; i++)
record->hits[i] = 0;
for (i = 0; i < MAX_SERVICES; i++)
record->drops[i] = 0;
for (i = 0; i < MAX_SERVICES; i++)
record->tokens[i] = max_tokens[i];
for (i = 0; i < MAX_SERVICES; i++)
record->rate[i] = INVALID_RATE;
}
record->ntp_timeout_rate = INVALID_RATE;
record->drop_flags = 0;

669
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2024
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2025
*
* 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,87 +68,6 @@ static int bound_sock_fd4;
/* Flag indicating whether this module has been initialised or not */
static int initialised = 0;
/* ================================================== */
/* Array of permission levels for command types */
static const char permissions[] = {
PERMIT_OPEN, /* NULL */
PERMIT_AUTH, /* ONLINE */
PERMIT_AUTH, /* OFFLINE */
PERMIT_AUTH, /* BURST */
PERMIT_AUTH, /* MODIFY_MINPOLL */
PERMIT_AUTH, /* MODIFY_MAXPOLL */
PERMIT_AUTH, /* DUMP */
PERMIT_AUTH, /* MODIFY_MAXDELAY */
PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */
PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */
PERMIT_OPEN, /* LOGON */
PERMIT_AUTH, /* SETTIME */
PERMIT_AUTH, /* LOCAL */
PERMIT_AUTH, /* MANUAL */
PERMIT_OPEN, /* N_SOURCES */
PERMIT_OPEN, /* SOURCE_DATA */
PERMIT_AUTH, /* REKEY */
PERMIT_AUTH, /* ALLOW */
PERMIT_AUTH, /* ALLOWALL */
PERMIT_AUTH, /* DENY */
PERMIT_AUTH, /* DENYALL */
PERMIT_AUTH, /* CMDALLOW */
PERMIT_AUTH, /* CMDALLOWALL */
PERMIT_AUTH, /* CMDDENY */
PERMIT_AUTH, /* CMDDENYALL */
PERMIT_AUTH, /* ACCHECK */
PERMIT_AUTH, /* CMDACCHECK */
PERMIT_AUTH, /* ADD_SERVER */
PERMIT_AUTH, /* ADD_PEER */
PERMIT_AUTH, /* DEL_SOURCE */
PERMIT_AUTH, /* WRITERTC */
PERMIT_AUTH, /* DFREQ */
PERMIT_AUTH, /* DOFFSET */
PERMIT_OPEN, /* TRACKING */
PERMIT_OPEN, /* SOURCESTATS */
PERMIT_OPEN, /* RTCREPORT */
PERMIT_AUTH, /* TRIMRTC */
PERMIT_AUTH, /* CYCLELOGS */
PERMIT_AUTH, /* SUBNETS_ACCESSED */
PERMIT_AUTH, /* CLIENT_ACCESSES (by subnet) */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX */
PERMIT_OPEN, /* MANUAL_LIST */
PERMIT_AUTH, /* MANUAL_DELETE */
PERMIT_AUTH, /* MAKESTEP */
PERMIT_OPEN, /* ACTIVITY */
PERMIT_AUTH, /* MODIFY_MINSTRATUM */
PERMIT_AUTH, /* MODIFY_POLLTARGET */
PERMIT_AUTH, /* MODIFY_MAXDELAYDEVRATIO */
PERMIT_AUTH, /* RESELECT */
PERMIT_AUTH, /* RESELECTDISTANCE */
PERMIT_AUTH, /* MODIFY_MAKESTEP */
PERMIT_OPEN, /* SMOOTHING */
PERMIT_AUTH, /* SMOOTHTIME */
PERMIT_AUTH, /* REFRESH */
PERMIT_AUTH, /* SERVER_STATS */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
PERMIT_AUTH, /* LOCAL2 */
PERMIT_AUTH, /* NTP_DATA */
PERMIT_AUTH, /* ADD_SERVER2 */
PERMIT_AUTH, /* ADD_PEER2 */
PERMIT_AUTH, /* ADD_SERVER3 */
PERMIT_AUTH, /* ADD_PEER3 */
PERMIT_AUTH, /* SHUTDOWN */
PERMIT_AUTH, /* ONOFFLINE */
PERMIT_AUTH, /* ADD_SOURCE */
PERMIT_OPEN, /* NTP_SOURCE_NAME */
PERMIT_AUTH, /* RESET_SOURCES */
PERMIT_AUTH, /* AUTH_DATA */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
PERMIT_AUTH, /* MODIFY_OFFSET */
PERMIT_AUTH, /* LOCAL3 */
};
/* ================================================== */
/* This authorisation table is used for checking whether particular
@@ -227,19 +146,17 @@ do_size_checks(void)
request.command = htons(i);
request_length = PKL_CommandLength(&request);
padding_length = PKL_CommandPaddingLength(&request);
if (padding_length > MAX_PADDING_LENGTH || padding_length > request_length ||
request_length > sizeof (CMD_Request) ||
(request_length && request_length < offsetof(CMD_Request, data)))
assert(0);
BRIEF_ASSERT(padding_length <= MAX_PADDING_LENGTH && padding_length <= request_length &&
request_length <= sizeof (CMD_Request) &&
(request_length == 0 || request_length >= offsetof(CMD_Request, data)));
}
for (i = 1; i < N_REPLY_TYPES; i++) {
reply.reply = htons(i);
reply.status = STT_SUCCESS;
reply_length = PKL_ReplyLength(&reply);
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
reply_length > sizeof (CMD_Reply))
assert(0);
BRIEF_ASSERT((reply_length == 0 || reply_length >= offsetof(CMD_Reply, data)) &&
reply_length <= sizeof (CMD_Reply));
}
}
@@ -249,7 +166,6 @@ void
CAM_Initialise(void)
{
assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks();
initialised = 1;
@@ -533,7 +449,9 @@ handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
ntohl(rx_message->data.local.orphan),
UTI_FloatNetworkToHost(rx_message->data.local.activate));
UTI_FloatNetworkToHost(rx_message->data.local.activate),
UTI_FloatNetworkToHost(rx_message->data.local.wait_synced),
UTI_FloatNetworkToHost(rx_message->data.local.wait_unsynced));
} else {
REF_DisableLocal();
}
@@ -768,6 +686,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
params.max_unreach = ntohl(rx_message->data.ntp_source.max_unreach);
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
@@ -1439,18 +1358,242 @@ handle_modify_offset(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static int
handle_readwrite_commands(int command, CMD_Request *request, CMD_Reply *reply)
{
switch (command) {
case REQ_ADD_SOURCE:
handle_add_source(request, reply);
break;
case REQ_ALLOW:
handle_allowdeny(request, reply, 1, 0);
break;
case REQ_ALLOWALL:
handle_allowdeny(request, reply, 1, 1);
break;
case REQ_BURST:
handle_burst(request, reply);
break;
case REQ_CMDALLOW:
handle_cmdallowdeny(request, reply, 1, 0);
break;
case REQ_CMDALLOWALL:
handle_cmdallowdeny(request, reply, 1, 1);
break;
case REQ_CMDDENY:
handle_cmdallowdeny(request, reply, 0, 0);
break;
case REQ_CMDDENYALL:
handle_cmdallowdeny(request, reply, 0, 1);
break;
case REQ_CYCLELOGS:
handle_cyclelogs(request, reply);
break;
case REQ_DEL_SOURCE:
handle_del_source(request, reply);
break;
case REQ_DENY:
handle_allowdeny(request, reply, 0, 0);
break;
case REQ_DENYALL:
handle_allowdeny(request, reply, 0, 1);
break;
case REQ_DFREQ:
handle_dfreq(request, reply);
break;
case REQ_DOFFSET2:
handle_doffset(request, reply);
break;
case REQ_DUMP:
handle_dump(request, reply);
break;
case REQ_LOCAL3:
handle_local(request, reply);
break;
case REQ_MAKESTEP:
handle_make_step(request, reply);
break;
case REQ_MANUAL:
handle_manual(request, reply);
break;
case REQ_MANUAL_DELETE:
handle_manual_delete(request, reply);
break;
case REQ_MODIFY_MAKESTEP:
handle_modify_makestep(request, reply);
break;
case REQ_MODIFY_MAXDELAY:
handle_modify_maxdelay(request, reply);
break;
case REQ_MODIFY_MAXDELAYDEVRATIO:
handle_modify_maxdelaydevratio(request, reply);
break;
case REQ_MODIFY_MAXDELAYRATIO:
handle_modify_maxdelayratio(request, reply);
break;
case REQ_MODIFY_MAXPOLL:
handle_modify_maxpoll(request, reply);
break;
case REQ_MODIFY_MAXUPDATESKEW:
handle_modify_maxupdateskew(request, reply);
break;
case REQ_MODIFY_MINPOLL:
handle_modify_minpoll(request, reply);
break;
case REQ_MODIFY_MINSTRATUM:
handle_modify_minstratum(request, reply);
break;
case REQ_MODIFY_OFFSET:
handle_modify_offset(request, reply);
break;
case REQ_MODIFY_POLLTARGET:
handle_modify_polltarget(request, reply);
break;
case REQ_MODIFY_SELECTOPTS:
handle_modify_selectopts(request, reply);
break;
case REQ_OFFLINE:
handle_offline(request, reply);
break;
case REQ_ONLINE:
handle_online(request, reply);
break;
case REQ_ONOFFLINE:
handle_onoffline(request, reply);
break;
case REQ_REFRESH:
handle_refresh(request, reply);
break;
case REQ_REKEY:
handle_rekey(request, reply);
break;
case REQ_RELOAD_SOURCES:
handle_reload_sources(request, reply);
break;
case REQ_RESELECT:
handle_reselect(request, reply);
break;
case REQ_RESELECTDISTANCE:
handle_reselect_distance(request, reply);
break;
case REQ_RESET_SOURCES:
handle_reset_sources(request, reply);
break;
case REQ_SETTIME:
handle_settime(request, reply);
break;
case REQ_SHUTDOWN:
handle_shutdown(request, reply);
break;
case REQ_SMOOTHTIME:
handle_smoothtime(request, reply);
break;
case REQ_TRIMRTC:
handle_trimrtc(request, reply);
break;
case REQ_WRITERTC:
handle_writertc(request, reply);
break;
default:
return 0;
}
return 1;
}
/* ================================================== */
static int
handle_readonly_commands(int command, int full_access, CMD_Request *request, CMD_Reply *reply)
{
ARR_Instance open_commands;
int i, allowed = 0;
if (full_access) {
allowed = 1;
} else {
open_commands = CNF_GetOpenCommands();
for (i = 0; i < ARR_GetSize(open_commands); i++) {
if (*(int *)ARR_GetElement(open_commands, i) == command) {
allowed = 1;
break;
}
}
}
if (!allowed)
return 0;
switch (command) {
case REQ_ACCHECK:
handle_accheck(request, reply);
break;
case REQ_ACTIVITY:
handle_activity(request, reply);
break;
case REQ_AUTH_DATA:
handle_auth_data(request, reply);
break;
case REQ_CLIENT_ACCESSES_BY_INDEX3:
handle_client_accesses_by_index(request, reply);
break;
case REQ_CMDACCHECK:
handle_cmdaccheck(request, reply);
break;
case REQ_MANUAL_LIST:
handle_manual_list(request, reply);
break;
case REQ_NTP_DATA:
handle_ntp_data(request, reply);
break;
case REQ_NTP_SOURCE_NAME:
handle_ntp_source_name(request, reply);
break;
case REQ_N_SOURCES:
handle_n_sources(request, reply);
break;
case REQ_RTCREPORT:
handle_rtcreport(request, reply);
break;
case REQ_SELECT_DATA:
handle_select_data(request, reply);
break;
case REQ_SERVER_STATS:
handle_server_stats(request, reply);
break;
case REQ_SMOOTHING:
handle_smoothing(request, reply);
break;
case REQ_SOURCESTATS:
handle_sourcestats(request, reply);
break;
case REQ_SOURCE_DATA:
handle_source_data(request, reply);
break;
case REQ_TRACKING:
handle_tracking(request, reply);
break;
default:
return 0;
}
return 1;
}
/* ================================================== */
/* Read a packet and process it */
static void
read_from_cmd_socket(int sock_fd, int event, void *anything)
{
int read_length, expected_length, localhost, log_index, full_access, handled;
SCK_Message *sck_message;
CMD_Request rx_message;
CMD_Reply tx_message;
IPAddr loopback_addr, remote_ip;
int read_length, expected_length;
int localhost, allowed, log_index;
uint16_t rx_command;
struct timespec now, cooked_now;
@@ -1463,32 +1606,27 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* Get current time cheaply */
SCH_GetLastEventTime(&cooked_now, NULL, &now);
/* Check if it's from localhost (127.0.0.1, ::1, or Unix domain),
or an authorised address */
switch (sck_message->addr_type) {
case SCK_ADDR_IP:
assert(sock_fd == sock_fd4 || sock_fd == sock_fd6);
remote_ip = sck_message->remote_addr.ip.ip_addr;
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
/* Check if the request came from the Unix domain socket, or network and
whether the address is allowed (127.0.0.1 and ::1 is always allowed) */
if ((sock_fd == sock_fd4 || sock_fd == sock_fd6) && sck_message->addr_type == SCK_ADDR_IP) {
remote_ip = sck_message->remote_addr.ip.ip_addr;
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
DEBUG_LOG("Unauthorised host %s",
UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
return;
}
assert(remote_ip.family != IPADDR_UNSPEC);
break;
case SCK_ADDR_UNIX:
assert(sock_fd == sock_fdu);
remote_ip.family = IPADDR_UNSPEC;
localhost = 1;
break;
default:
DEBUG_LOG("Unexpected address type");
if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
DEBUG_LOG("Unauthorised host %s",
UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
return;
}
full_access = 0;
} else if (sock_fd == sock_fdu && sck_message->addr_type == SCK_ADDR_UNIX) {
remote_ip.family = IPADDR_UNSPEC;
localhost = 1;
full_access = 1;
} else {
DEBUG_LOG("Unexpected socket/address");
return;
}
if (read_length < offsetof(CMD_Request, data) ||
@@ -1564,297 +1702,20 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* OK, we have a valid message. Now dispatch on message type and process it. */
if (rx_command >= N_REQUEST_TYPES) {
/* This should be already handled */
assert(0);
} else {
/* Check level of authority required to issue the command. All commands
from the Unix domain socket (which is accessible only by the root and
chrony user/group) are allowed. */
if (remote_ip.family == IPADDR_UNSPEC) {
assert(sock_fd == sock_fdu);
allowed = 1;
} else {
switch (permissions[rx_command]) {
case PERMIT_AUTH:
allowed = 0;
break;
case PERMIT_LOCAL:
allowed = localhost;
break;
case PERMIT_OPEN:
allowed = 1;
break;
default:
assert(0);
allowed = 0;
}
}
LOG_SetContext(LOGC_Command);
if (allowed) {
LOG_SetContext(LOGC_Command);
if (full_access)
handled = handle_readwrite_commands(rx_command, &rx_message, &tx_message);
else
handled = 0;
switch(rx_command) {
case REQ_NULL:
/* Do nothing */
break;
if (!handled)
handled = handle_readonly_commands(rx_command, full_access, &rx_message, &tx_message);
case REQ_DUMP:
handle_dump(&rx_message, &tx_message);
break;
if (!handled)
tx_message.status = htons(STT_UNAUTH);
case REQ_ONLINE:
handle_online(&rx_message, &tx_message);
break;
case REQ_OFFLINE:
handle_offline(&rx_message, &tx_message);
break;
case REQ_BURST:
handle_burst(&rx_message, &tx_message);
break;
case REQ_MODIFY_MINPOLL:
handle_modify_minpoll(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXPOLL:
handle_modify_maxpoll(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXDELAY:
handle_modify_maxdelay(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXDELAYRATIO:
handle_modify_maxdelayratio(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXDELAYDEVRATIO:
handle_modify_maxdelaydevratio(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXUPDATESKEW:
handle_modify_maxupdateskew(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAKESTEP:
handle_modify_makestep(&rx_message, &tx_message);
break;
case REQ_LOGON:
/* Authentication is no longer supported, log-on always fails */
tx_message.status = htons(STT_FAILED);
break;
case REQ_SETTIME:
handle_settime(&rx_message, &tx_message);
break;
case REQ_LOCAL3:
handle_local(&rx_message, &tx_message);
break;
case REQ_MANUAL:
handle_manual(&rx_message, &tx_message);
break;
case REQ_N_SOURCES:
handle_n_sources(&rx_message, &tx_message);
break;
case REQ_SOURCE_DATA:
handle_source_data(&rx_message, &tx_message);
break;
case REQ_REKEY:
handle_rekey(&rx_message, &tx_message);
break;
case REQ_ALLOW:
handle_allowdeny(&rx_message, &tx_message, 1, 0);
break;
case REQ_ALLOWALL:
handle_allowdeny(&rx_message, &tx_message, 1, 1);
break;
case REQ_DENY:
handle_allowdeny(&rx_message, &tx_message, 0, 0);
break;
case REQ_DENYALL:
handle_allowdeny(&rx_message, &tx_message, 0, 1);
break;
case REQ_CMDALLOW:
handle_cmdallowdeny(&rx_message, &tx_message, 1, 0);
break;
case REQ_CMDALLOWALL:
handle_cmdallowdeny(&rx_message, &tx_message, 1, 1);
break;
case REQ_CMDDENY:
handle_cmdallowdeny(&rx_message, &tx_message, 0, 0);
break;
case REQ_CMDDENYALL:
handle_cmdallowdeny(&rx_message, &tx_message, 0, 1);
break;
case REQ_ACCHECK:
handle_accheck(&rx_message, &tx_message);
break;
case REQ_CMDACCHECK:
handle_cmdaccheck(&rx_message, &tx_message);
break;
case REQ_ADD_SOURCE:
handle_add_source(&rx_message, &tx_message);
break;
case REQ_DEL_SOURCE:
handle_del_source(&rx_message, &tx_message);
break;
case REQ_WRITERTC:
handle_writertc(&rx_message, &tx_message);
break;
case REQ_DFREQ:
handle_dfreq(&rx_message, &tx_message);
break;
case REQ_DOFFSET2:
handle_doffset(&rx_message, &tx_message);
break;
case REQ_TRACKING:
handle_tracking(&rx_message, &tx_message);
break;
case REQ_SMOOTHING:
handle_smoothing(&rx_message, &tx_message);
break;
case REQ_SMOOTHTIME:
handle_smoothtime(&rx_message, &tx_message);
break;
case REQ_SOURCESTATS:
handle_sourcestats(&rx_message, &tx_message);
break;
case REQ_RTCREPORT:
handle_rtcreport(&rx_message, &tx_message);
break;
case REQ_TRIMRTC:
handle_trimrtc(&rx_message, &tx_message);
break;
case REQ_CYCLELOGS:
handle_cyclelogs(&rx_message, &tx_message);
break;
case REQ_CLIENT_ACCESSES_BY_INDEX3:
handle_client_accesses_by_index(&rx_message, &tx_message);
break;
case REQ_MANUAL_LIST:
handle_manual_list(&rx_message, &tx_message);
break;
case REQ_MANUAL_DELETE:
handle_manual_delete(&rx_message, &tx_message);
break;
case REQ_MAKESTEP:
handle_make_step(&rx_message, &tx_message);
break;
case REQ_ACTIVITY:
handle_activity(&rx_message, &tx_message);
break;
case REQ_RESELECTDISTANCE:
handle_reselect_distance(&rx_message, &tx_message);
break;
case REQ_RESELECT:
handle_reselect(&rx_message, &tx_message);
break;
case REQ_MODIFY_MINSTRATUM:
handle_modify_minstratum(&rx_message, &tx_message);
break;
case REQ_MODIFY_POLLTARGET:
handle_modify_polltarget(&rx_message, &tx_message);
break;
case REQ_REFRESH:
handle_refresh(&rx_message, &tx_message);
break;
case REQ_SERVER_STATS:
handle_server_stats(&rx_message, &tx_message);
break;
case REQ_NTP_DATA:
handle_ntp_data(&rx_message, &tx_message);
break;
case REQ_SHUTDOWN:
handle_shutdown(&rx_message, &tx_message);
break;
case REQ_ONOFFLINE:
handle_onoffline(&rx_message, &tx_message);
break;
case REQ_NTP_SOURCE_NAME:
handle_ntp_source_name(&rx_message, &tx_message);
break;
case REQ_RESET_SOURCES:
handle_reset_sources(&rx_message, &tx_message);
break;
case REQ_AUTH_DATA:
handle_auth_data(&rx_message, &tx_message);
break;
case REQ_SELECT_DATA:
handle_select_data(&rx_message, &tx_message);
break;
case REQ_RELOAD_SOURCES:
handle_reload_sources(&rx_message, &tx_message);
break;
case REQ_MODIFY_SELECTOPTS:
handle_modify_selectopts(&rx_message, &tx_message);
break;
case REQ_MODIFY_OFFSET:
handle_modify_offset(&rx_message, &tx_message);
break;
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}
LOG_UnsetContext(LOGC_Command);
} else {
tx_message.status = htons(STT_UNAUTH);
}
}
LOG_UnsetContext(LOGC_Command);
/* Transmit the response */
transmit_reply(sock_fd, read_length, sck_message);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021, 2025
*
* 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,7 +39,12 @@
/* ================================================== */
int
#define SSCANF_IN_RANGE(s, f, x, n, min, max) \
(sscanf((s), (f), (x), (n)) == 1 && *(x) >= (min) && *(x) <= (max))
/* ================================================== */
CPS_Status
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
@@ -61,6 +66,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.max_unreach = SRC_DEFAULT_MAXUNREACH;
src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
@@ -82,7 +88,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
line = CPS_SplitWord(line);
if (!*hostname)
return 0;
return CPS_MissingArgument;
src->name = hostname;
@@ -104,17 +110,17 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "extfield")) {
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
return 0;
return CPS_InvalidValue;
switch (ef_type) {
case NTP_EF_EXP_MONO_ROOT:
src->params.ext_fields |= NTP_EF_FLAG_EXP_MONO_ROOT;
@@ -123,78 +129,81 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.ext_fields |= NTP_EF_FLAG_EXP_NET_CORRECTION;
break;
default:
return 0;
return CPS_InvalidValue;
}
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.filter_length, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "ipv4")) {
src->family = IPADDR_INET4;
} else if (!strcasecmp(cmd, "ipv6")) {
src->family = IPADDR_INET6;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxdelayquant")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.maxpoll, &n, -32, 32))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_samples, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_sources, &n, 1, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "maxunreach")) {
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_unreach, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "mindelay")) {
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_samples, &n, 0, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_stratum, &n, 0, NTP_MAX_STRATUM))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "nts")) {
src->params.nts = 1;
} else if (!strcasecmp(cmd, "ntsport")) {
if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.nts_port, &n, 0, 65535))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "offset")) {
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%d%n", &src->port, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->port, &n, 0, 65535))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.poll_target, &n, 1, INT_MAX))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.presend_minpoll, &n, -32, 32))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.version, &n, 1, NTP_VERSION))
return CPS_InvalidValue;
} 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;
return CPS_InvalidOption;
}
}
return 1;
return CPS_Success;
}
/* ================================================== */
@@ -295,8 +304,9 @@ CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate)
CPS_Status
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate,
double *wait_synced, double *wait_unsynced)
{
int n;
char *cmd;
@@ -305,32 +315,42 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *
*distance = 1.0;
*activate = 0.0;
*orphan = 0;
*wait_synced = 0;
*wait_unsynced = -1.0;
while (*line) {
cmd = line;
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", stratum, &n) != 1 ||
*stratum >= NTP_MAX_STRATUM || *stratum <= 0)
return 0;
if (!SSCANF_IN_RANGE(line, "%d%n", stratum, &n, 1, NTP_MAX_STRATUM - 1))
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "orphan")) {
*orphan = 1;
n = 0;
} else if (!strcasecmp(cmd, "distance")) {
if (sscanf(line, "%lf%n", distance, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "activate")) {
if (sscanf(line, "%lf%n", activate, &n) != 1)
return 0;
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "waitsynced")) {
if (sscanf(line, "%lf%n", wait_synced, &n) != 1)
return CPS_InvalidValue;
} else if (!strcasecmp(cmd, "waitunsynced")) {
if (sscanf(line, "%lf%n", wait_unsynced, &n) != 1)
return CPS_InvalidValue;
} else {
return 0;
return CPS_InvalidOption;
}
line += n;
}
return 1;
if (*wait_unsynced < 0.0)
*wait_unsynced = *orphan ? 300 : 0.0;
return CPS_Success;
}
/* ================================================== */

View File

@@ -30,6 +30,13 @@
#include "srcparams.h"
#include "addressing.h"
typedef enum {
CPS_Success,
CPS_InvalidValue,
CPS_InvalidOption,
CPS_MissingArgument,
} CPS_Status;
typedef struct {
char *name;
int family;
@@ -38,7 +45,7 @@ typedef struct {
} CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Get an NTP/refclock select option */
extern int CPS_GetSelectOption(char *option);
@@ -47,7 +54,8 @@ extern int CPS_GetSelectOption(char *option);
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate);
extern CPS_Status CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance,
double *activate, double *wait_synced, double *wait_unsynced);
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);

349
conf.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2017, 2020, 2024
* Copyright (C) Miroslav Lichvar 2009-2017, 2020, 2024-2025
*
* 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
@@ -50,14 +50,17 @@
#define MAX_CONF_DIRS 10
#define MAX_INCLUDE_LEVEL 10
#define SSCANF_IN_RANGE(s, f, x, n, min, max) \
(sscanf((s), (f), (x), (n)) == 1 && *(x) >= (min) && *(x) <= (max))
/* ================================================== */
/* Forward prototypes */
static int parse_string(char *line, char **result);
static int parse_int(char *line, int *result);
static int parse_double(char *line, double *result);
static int parse_null(char *line);
static int parse_ints(char *line, ARR_Instance array);
static void parse_string(char *line, char **result);
static void parse_int(char *line, int *result, int min, int max);
static void parse_double(char *line, double *result);
static void parse_null(char *line, int *result);
static void parse_ints(char *line, ARR_Instance array, int min, int max);
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
static void parse_authselectmode(char *);
@@ -67,6 +70,7 @@ static void parse_bindcmdaddress(char *);
static void parse_broadcast(char *);
static void parse_clientloglimit(char *);
static void parse_confdir(char *);
static void parse_driftfile(char *);
static void parse_fallbackdrift(char *);
static void parse_hwtimestamp(char *);
static void parse_include(char *);
@@ -79,6 +83,7 @@ static void parse_makestep(char *);
static void parse_maxchange(char *);
static void parse_ntsserver(char *, ARR_Instance files);
static void parse_ntstrustedcerts(char *);
static void parse_open_commands(char *line);
static void parse_pidfile(char *line);
static void parse_ratelimit(char *line, int *enabled, int *interval,
int *burst, int *leak, int *kod);
@@ -98,6 +103,7 @@ static int acquisition_port = -1;
static int ntp_port = NTP_PORT;
static char *keys_file = NULL;
static char *drift_file = NULL;
static int drift_file_interval = 3600;
static char *rtc_file = NULL;
static double max_update_skew = 1000.0;
static double correction_time_ratio = 3.0;
@@ -109,6 +115,8 @@ static double clock_precision = 0.0; /* in seconds */
static SRC_AuthSelectMode authselect_mode = SRC_AUTHSELECT_MIX;
static double max_distance = 3.0;
static double max_jitter = 1.0;
static int max_stratum = NTP_MAX_STRATUM - 1;
static int min_stratum = 0;
static double reselect_distance = 1e-4;
static double stratum_weight = 1e-3;
static double combine_limit = 3.0;
@@ -132,6 +140,8 @@ static int local_stratum;
static int local_orphan;
static double local_distance;
static double local_activate;
static double local_wait_synced;
static double local_wait_unsynced;
/* Threshold (in seconds) - if absolute value of initial error is less
than this, slew instead of stepping */
@@ -324,6 +334,11 @@ typedef struct _AllowDeny {
static ARR_Instance ntp_restrictions;
static ARR_Instance cmd_restrictions;
#define DEFAULT_OPEN_COMMANDS "activity manual rtcdata smoothing sourcename sources sourcestats tracking"
/* Array of int specifying commands allowed from network */
static ARR_Instance open_commands;
typedef struct {
NTP_Remote_Address addr;
int interval;
@@ -353,12 +368,20 @@ command_parse_error(void)
/* ================================================== */
FORMAT_ATTRIBUTE_PRINTF(1, 2)
static void
other_parse_error(const char *message)
other_parse_error(const char *format, ...)
{
LOG_FATAL("%s at line %d%s%s",
message, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
char buf[256];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof (buf), format, ap);
va_end(ap);
LOG_FATAL("%s at line %d%s%s",
buf, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
}
/* ================================================== */
@@ -401,7 +424,7 @@ check_number_of_args(char *line, int num)
void
CNF_Initialise(int r, int client_only)
{
char buf[10];
char buf[128];
restarted = r;
@@ -416,9 +439,13 @@ CNF_Initialise(int r, int client_only)
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
open_commands = ARR_CreateInstance(sizeof (int));
snprintf(buf, sizeof (buf), DEFAULT_OPEN_COMMANDS);
parse_open_commands(buf);
nts_aeads = ARR_CreateInstance(sizeof (int));
snprintf(buf, sizeof (buf), DEFAULT_NTS_AEADS);
parse_ints(buf, nts_aeads);
parse_ints(buf, nts_aeads, 0, INT_MAX);
nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
nts_server_key_files = ARR_CreateInstance(sizeof (char *));
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
@@ -475,6 +502,8 @@ CNF_Finalise(void)
ARR_DestroyInstance(refclock_sources);
ARR_DestroyInstance(broadcasts);
ARR_DestroyInstance(open_commands);
ARR_DestroyInstance(ntp_restrictions);
ARR_DestroyInstance(cmd_restrictions);
@@ -573,7 +602,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
printf("%s%s%s\n", command, p[0] != '\0' ? " " : "", p);
if (!strcasecmp(command, "acquisitionport")) {
parse_int(p, &acquisition_port);
parse_int(p, &acquisition_port, 0, 65535);
} else if (!strcasecmp(command, "allow")) {
parse_allow_deny(p, ntp_restrictions, 1);
} else if (!strcasecmp(command, "authselectmode")) {
@@ -601,7 +630,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "cmddeny")) {
parse_allow_deny(p, cmd_restrictions, 0);
} else if (!strcasecmp(command, "cmdport")) {
parse_int(p, &cmd_port);
parse_int(p, &cmd_port, 0, 65535);
} else if (!strcasecmp(command, "cmdratelimit")) {
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
&cmd_ratelimit_burst, &cmd_ratelimit_leak, NULL);
@@ -614,9 +643,9 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "deny")) {
parse_allow_deny(p, ntp_restrictions, 0);
} else if (!strcasecmp(command, "driftfile")) {
parse_string(p, &drift_file);
parse_driftfile(p);
} else if (!strcasecmp(command, "dscp")) {
parse_int(p, &ntp_dscp);
parse_int(p, &ntp_dscp, 0, 63);
} else if (!strcasecmp(command, "dumpdir")) {
parse_string(p, &dumpdir);
} else if (!strcasecmp(command, "dumponexit")) {
@@ -644,11 +673,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "local")) {
parse_local(p);
} else if (!strcasecmp(command, "lock_all")) {
lock_memory = parse_null(p);
parse_null(p, &lock_memory);
} else if (!strcasecmp(command, "log")) {
parse_log(p);
} else if (!strcasecmp(command, "logbanner")) {
parse_int(p, &log_banner);
parse_int(p, &log_banner, 0, INT_MAX);
} else if (!strcasecmp(command, "logchange")) {
parse_double(p, &log_change_threshold);
} else if (!strcasecmp(command, "logdir")) {
@@ -658,7 +687,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "makestep")) {
parse_makestep(p);
} else if (!strcasecmp(command, "manual")) {
enable_manual = parse_null(p);
parse_null(p, &enable_manual);
} else if (!strcasecmp(command, "maxchange")) {
parse_maxchange(p);
} else if (!strcasecmp(command, "maxclockerror")) {
@@ -670,27 +699,31 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "maxjitter")) {
parse_double(p, &max_jitter);
} else if (!strcasecmp(command, "maxntsconnections")) {
parse_int(p, &nts_server_connections);
parse_int(p, &nts_server_connections, 1, INT_MAX);
} else if (!strcasecmp(command, "maxsamples")) {
parse_int(p, &max_samples);
parse_int(p, &max_samples, 0, INT_MAX);
} else if (!strcasecmp(command, "maxslewrate")) {
parse_double(p, &max_slew_rate);
} else if (!strcasecmp(command, "maxstratum")) {
parse_int(p, &max_stratum, 0, INT_MAX);
} else if (!strcasecmp(command, "maxupdateskew")) {
parse_double(p, &max_update_skew);
} else if (!strcasecmp(command, "minsamples")) {
parse_int(p, &min_samples);
parse_int(p, &min_samples, 0, INT_MAX);
} else if (!strcasecmp(command, "minsources")) {
parse_int(p, &min_sources);
parse_int(p, &min_sources, 1, INT_MAX);
} else if (!strcasecmp(command, "minstratum")) {
parse_int(p, &min_stratum, 0, INT_MAX);
} else if (!strcasecmp(command, "nocerttimecheck")) {
parse_int(p, &no_cert_time_check);
parse_int(p, &no_cert_time_check, 0, INT_MAX);
} else if (!strcasecmp(command, "noclientlog")) {
no_client_log = parse_null(p);
parse_null(p, &no_client_log);
} else if (!strcasecmp(command, "nosystemcert")) {
no_system_cert = parse_null(p);
parse_null(p, &no_system_cert);
} else if (!strcasecmp(command, "ntpsigndsocket")) {
parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "ntsaeads")) {
parse_ints(p, nts_aeads);
parse_ints(p, nts_aeads, 0, INT_MAX);
} else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
@@ -700,19 +733,21 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "ntsntpserver")) {
parse_string(p, &nts_ntp_server);
} else if (!strcasecmp(command, "ntsport")) {
parse_int(p, &nts_server_port);
parse_int(p, &nts_server_port, 0, 65535);
} else if (!strcasecmp(command, "ntsprocesses")) {
parse_int(p, &nts_server_processes);
parse_int(p, &nts_server_processes, 0, 1000);
} else if (!strcasecmp(command, "ntsrefresh")) {
parse_int(p, &nts_refresh);
parse_int(p, &nts_refresh, 0, INT_MAX);
} else if (!strcasecmp(command, "ntsrotate")) {
parse_int(p, &nts_rotate);
parse_int(p, &nts_rotate, 0, INT_MAX);
} else if (!strcasecmp(command, "ntsservercert")) {
parse_ntsserver(p, nts_server_cert_files);
} else if (!strcasecmp(command, "ntsserverkey")) {
parse_ntsserver(p, nts_server_key_files);
} else if (!strcasecmp(command, "ntstrustedcerts")) {
parse_ntstrustedcerts(p);
} else if (!strcasecmp(command, "opencommands")) {
parse_open_commands(p);
} else if (!strcasecmp(command, "peer")) {
parse_source(p, command, 1);
} else if (!strcasecmp(command, "pidfile")) {
@@ -720,18 +755,18 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "pool")) {
parse_source(p, command, 1);
} else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port);
parse_int(p, &ntp_port, 0, 65535);
} else if (!strcasecmp(command, "ptpdomain")) {
parse_int(p, &ptp_domain);
parse_int(p, &ptp_domain, 0, 255);
} else if (!strcasecmp(command, "ptpport")) {
parse_int(p, &ptp_port);
parse_int(p, &ptp_port, 0, 65535);
} else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
&ntp_ratelimit_burst, &ntp_ratelimit_leak, &ntp_ratelimit_kod);
} else if (!strcasecmp(command, "refclock")) {
parse_refclock(p);
} else if (!strcasecmp(command, "refresh")) {
parse_int(p, &refresh);
parse_int(p, &refresh, 0, INT_MAX);
} else if (!strcasecmp(command, "reselectdist")) {
parse_double(p, &reselect_distance);
} else if (!strcasecmp(command, "rtcautotrim")) {
@@ -741,11 +776,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "rtcfile")) {
parse_string(p, &rtc_file);
} else if (!strcasecmp(command, "rtconutc")) {
rtc_on_utc = parse_null(p);
parse_null(p, &rtc_on_utc);
} else if (!strcasecmp(command, "rtcsync")) {
rtc_sync = parse_null(p);
parse_null(p, &rtc_sync);
} else if (!strcasecmp(command, "sched_priority")) {
parse_int(p, &sched_priority);
parse_int(p, &sched_priority, 0, 100);
} else if (!strcasecmp(command, "server")) {
parse_source(p, command, 1);
} else if (!strcasecmp(command, "smoothtime")) {
@@ -764,7 +799,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
!strcasecmp(command, "linux_hz")) {
LOG(LOGS_WARN, "%s directive is no longer supported", command);
} else {
other_parse_error("Invalid directive");
other_parse_error("Invalid directive %s", command);
}
processed_file = processed_command = NULL;
@@ -772,54 +807,58 @@ CNF_ParseLine(const char *filename, int number, char *line)
/* ================================================== */
static int
static void
parse_string(char *line, char **result)
{
check_number_of_args(line, 1);
Free(*result);
*result = Strdup(line);
return 1;
}
/* ================================================== */
static int
parse_int(char *line, int *result)
static void
parse_int(char *line, int *result, int min, int max)
{
char *end;
long r;
check_number_of_args(line, 1);
if (sscanf(line, "%d", result) != 1) {
errno = 0;
r = strtol(line, &end, 10);
if (errno != 0 || *end != '\0')
command_parse_error();
return 0;
}
return 1;
if (r < min || r > max)
other_parse_error("Invalid value %ld in %s directive (min %d, max %d)",
r, processed_command, min, max);
*result = r;
}
/* ================================================== */
static int
static void
parse_double(char *line, double *result)
{
check_number_of_args(line, 1);
if (sscanf(line, "%lf", result) != 1) {
command_parse_error();
return 0;
}
return 1;
}
/* ================================================== */
static int
parse_null(char *line)
static void
parse_null(char *line, int *result)
{
check_number_of_args(line, 0);
return 1;
*result = 1;
}
/* ================================================== */
static int
parse_ints(char *line, ARR_Instance array)
static void
parse_ints(char *line, ARR_Instance array, int min, int max)
{
char *s;
int v;
@@ -829,10 +868,9 @@ parse_ints(char *line, ARR_Instance array)
while (*line) {
s = line;
line = CPS_SplitWord(line);
parse_int(s, &v);
parse_int(s, &v, min, max);
ARR_AppendElement(array, &v);
}
return 1;
}
/* ================================================== */
@@ -840,6 +878,7 @@ parse_ints(char *line, ARR_Instance array)
static void
parse_source(char *line, char *type, int fatal)
{
CPS_Status status;
NTP_Source source;
if (strcasecmp(type, "peer") == 0) {
@@ -860,9 +899,13 @@ parse_source(char *line, char *type, int fatal)
/* Avoid comparing uninitialized data in compare_sources() */
memset(&source.params, 0, sizeof (source.params));
if (!CPS_ParseNTPSourceAdd(line, &source.params)) {
if (fatal)
command_parse_error();
status = CPS_ParseNTPSourceAdd(line, &source.params);
if (status != CPS_Success) {
if (fatal) {
other_parse_error("Invalid %s %s directive",
status == CPS_InvalidOption ? "option in" :
status == CPS_InvalidValue ? "value in" : "syntax for", type);
}
return;
}
@@ -897,7 +940,7 @@ parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak,
while (*line) {
opt = line;
line = CPS_SplitWord(line);
if (sscanf(line, "%d%n", &val, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &val, &n, -32, 32)) {
command_parse_error();
return;
}
@@ -921,7 +964,7 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int local, max_lock_age, pps_forced, sel_option, stratum, tai;
int local, max_lock_age, max_unreach, 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;
@@ -935,6 +978,7 @@ parse_refclock(char *line)
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
max_samples = SRC_DEFAULT_MAXSAMPLES;
max_unreach = SRC_DEFAULT_MAXUNREACH;
sel_options = 0;
offset = 0.0;
delay = 1e-9;
@@ -976,31 +1020,31 @@ parse_refclock(char *line)
if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
break;
} else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &poll, &n, -32, 32))
break;
}
} else if (!strcasecmp(cmd, "dpoll")) {
if (sscanf(line, "%d%n", &dpoll, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &dpoll, &n, -32, 32))
break;
}
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
if (!SSCANF_IN_RANGE(line, "%d%n", &filter_length, &n, 0, INT_MAX))
break;
}
} else if (!strcasecmp(cmd, "local")) {
n = 0;
local = 1;
} else if (!strcasecmp(cmd, "rate")) {
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &pps_rate, &n, 1, INT_MAX))
break;
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &min_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &min_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "maxlockage")) {
if (sscanf(line, "%d%n", &max_lock_age, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &max_lock_age, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &max_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &max_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "maxunreach")) {
if (!SSCANF_IN_RANGE(line, "%d%n", &max_unreach, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(cmd, "offset")) {
if (sscanf(line, "%lf%n", &offset, &n) != 1)
@@ -1018,8 +1062,7 @@ parse_refclock(char *line)
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
break;
} else if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", &stratum, &n) != 1 ||
stratum >= NTP_MAX_STRATUM || stratum < 0)
if (!SSCANF_IN_RANGE(line, "%d%n", &stratum, &n, 0, NTP_MAX_STRATUM - 1))
break;
} else if (!strcasecmp(cmd, "tai")) {
n = 0;
@@ -1031,13 +1074,13 @@ parse_refclock(char *line)
n = 0;
sel_options |= sel_option;
} else {
other_parse_error("Invalid refclock option");
other_parse_error("Invalid %s %s directive", "option in", processed_command);
return;
}
}
if (*cmd) {
command_parse_error();
other_parse_error("Invalid %s %s directive", "value in", processed_command);
return;
}
@@ -1052,6 +1095,7 @@ parse_refclock(char *line)
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
refclock->max_samples = max_samples;
refclock->max_unreach = max_unreach;
refclock->sel_options = sel_options;
refclock->stratum = stratum;
refclock->tai = tai;
@@ -1107,8 +1151,16 @@ parse_log(char *line)
static void
parse_local(char *line)
{
if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance, &local_activate))
command_parse_error();
CPS_Status status;
status = CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance,
&local_activate, &local_wait_synced, &local_wait_unsynced);
if (status != CPS_Success) {
other_parse_error("Invalid %s %s directive",
status == CPS_InvalidOption ? "option in" : "value in",
processed_command);
}
enable_local = 1;
}
@@ -1269,6 +1321,72 @@ parse_ntstrustedcerts(char *line)
/* ================================================== */
static void
add_open_command(int command)
{
int i;
/* Avoid duplicates */
for (i = 0; i < ARR_GetSize(open_commands); i++) {
if (*(int *)ARR_GetElement(open_commands, i) == command)
return;
}
ARR_AppendElement(open_commands, &command);
}
/* ================================================== */
static void
parse_open_commands(char *line)
{
char *s;
ARR_SetSize(open_commands, 0);
while (*line) {
s = line;
line = CPS_SplitWord(line);
if (strcasecmp(s, "activity") == 0) {
add_open_command(REQ_ACTIVITY);
} else if (strcasecmp(s, "authdata") == 0) {
add_open_command(REQ_N_SOURCES);
add_open_command(REQ_AUTH_DATA);
} else if (strcasecmp(s, "clients") == 0) {
add_open_command(REQ_CLIENT_ACCESSES_BY_INDEX3);
} else if (strcasecmp(s, "manual") == 0) {
add_open_command(REQ_MANUAL_LIST);
} else if (strcasecmp(s, "ntpdata") == 0) {
add_open_command(REQ_N_SOURCES);
add_open_command(REQ_NTP_DATA);
} else if (strcasecmp(s, "rtcdata") == 0) {
add_open_command(REQ_RTCREPORT);
} else if (strcasecmp(s, "selectdata") == 0) {
add_open_command(REQ_N_SOURCES);
add_open_command(REQ_SELECT_DATA);
} else if (strcasecmp(s, "serverstats") == 0) {
add_open_command(REQ_SERVER_STATS);
} else if (strcasecmp(s, "smoothing") == 0) {
add_open_command(REQ_SMOOTHING);
} else if (strcasecmp(s, "sourcename") == 0) {
add_open_command(REQ_NTP_SOURCE_NAME);
} else if (strcasecmp(s, "sources") == 0) {
add_open_command(REQ_N_SOURCES);
add_open_command(REQ_SOURCE_DATA);
} else if (strcasecmp(s, "sourcestats") == 0) {
add_open_command(REQ_N_SOURCES);
add_open_command(REQ_SOURCESTATS);
} else if (strcasecmp(s, "tracking") == 0) {
add_open_command(REQ_TRACKING);
} else {
command_parse_error();
}
}
}
/* ================================================== */
static void
parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
{
@@ -1510,17 +1628,17 @@ parse_hwtimestamp(char *line)
line = CPS_SplitWord(line);
if (!strcasecmp(p, "maxsamples")) {
if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->max_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->minpoll, &n, -32, 32))
break;
} else if (!strcasecmp(p, "maxpoll")) {
if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->maxpoll, &n, -32, 32))
break;
maxpoll_set = 1;
} else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->min_samples, &n, 0, INT_MAX))
break;
} else if (!strcasecmp(p, "precision")) {
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
@@ -1655,6 +1773,29 @@ parse_confdir(char *line)
/* ================================================== */
static void
parse_driftfile(char *line)
{
char *path, *opt, *val;
path = line;
opt = CPS_SplitWord(path);
val = CPS_SplitWord(opt);
if (*path == '\0' ||
(*opt != '\0' && (strcasecmp(opt, "interval") != 0 ||
sscanf(val, "%d", &drift_file_interval) != 1 ||
*CPS_SplitWord(val) != '\0'))) {
command_parse_error();
return;
}
Free(drift_file);
drift_file = Strdup(path);
}
/* ================================================== */
static void
parse_include(char *line)
{
@@ -1742,8 +1883,8 @@ reload_source_dirs(void)
NTP_Source *prev_sources, *new_sources, *source;
unsigned int i, j, prev_size, new_size, unresolved;
char buf[MAX_LINE_LENGTH];
int d, pass, was_added;
NSR_Status s;
int d, pass;
/* Ignore reload command before adding configured sources */
if (!conf_ntp_sources_added)
@@ -1782,13 +1923,16 @@ reload_source_dirs(void)
else
d = i < prev_size ? -1 : 1;
was_added = d <= 0 && (prev_sources[i].status == NSR_Success ||
prev_sources[i].status == NSR_UnresolvedName);
/* Remove missing sources before adding others to avoid conflicts */
if (pass == 0 && d < 0 && prev_sources[i].status == NSR_Success) {
if (pass == 0 && d < 0 && was_added) {
NSR_RemoveSourcesById(prev_sources[i].conf_id);
}
/* Add new sources and sources that could not be added before */
if (pass == 1 && (d > 0 || (d == 0 && prev_sources[i].status != NSR_Success))) {
if (pass == 1 && (d > 0 || (d == 0 && !was_added))) {
source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
source->pool, source->type, &source->params.params,
@@ -1980,8 +2124,9 @@ CNF_GetAcquisitionPort(void)
/* ================================================== */
char *
CNF_GetDriftFile(void)
CNF_GetDriftFile(int *interval)
{
*interval = drift_file_interval;
return drift_file;
}
@@ -2172,6 +2317,22 @@ CNF_GetMaxJitter(void)
/* ================================================== */
int
CNF_GetMaxStratum(void)
{
return max_stratum;
}
/* ================================================== */
int
CNF_GetMinStratum(void)
{
return min_stratum;
}
/* ================================================== */
double
CNF_GetReselectDistance(void)
{
@@ -2204,6 +2365,14 @@ CNF_GetManualEnabled(void)
/* ================================================== */
ARR_Instance
CNF_GetOpenCommands(void)
{
return open_commands;
}
/* ================================================== */
int
CNF_GetCommandPort(void) {
return cmd_port;
@@ -2212,13 +2381,16 @@ CNF_GetCommandPort(void) {
/* ================================================== */
int
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate)
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
double *wait_synced, double *wait_unsynced)
{
if (enable_local) {
*stratum = local_stratum;
*orphan = local_orphan;
*distance = local_distance;
*activate = local_activate;
*wait_synced = local_wait_synced;
*wait_unsynced = local_wait_unsynced;
return 1;
} else {
return 0;
@@ -2714,8 +2886,7 @@ CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids)
*paths = ARR_GetElements(nts_trusted_certs_paths);
*ids = ARR_GetElements(nts_trusted_certs_ids);
if (ARR_GetSize(nts_trusted_certs_paths) != ARR_GetSize(nts_trusted_certs_ids))
assert(0);
BRIEF_ASSERT(ARR_GetSize(nts_trusted_certs_paths) == ARR_GetSize(nts_trusted_certs_ids));
return ARR_GetSize(nts_trusted_certs_paths);
}

8
conf.h
View File

@@ -56,7 +56,7 @@ extern void CNF_ReloadSources(void);
extern int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
extern char *CNF_GetDriftFile(int *interval);
extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
@@ -70,6 +70,7 @@ extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void);
extern int CNF_GetManualEnabled(void);
extern ARR_Instance CNF_GetOpenCommands(void);
extern int CNF_GetCommandPort(void);
extern int CNF_GetRtcOnUtc(void);
extern int CNF_GetRtcSync(void);
@@ -105,11 +106,14 @@ extern double CNF_GetClockPrecision(void);
extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void);
extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void);
extern int CNF_GetMaxStratum(void);
extern int CNF_GetMinStratum(void);
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
double *wait_synced, double *wait_unsynced);
extern void CNF_SetupAccessRestrictions(void);

114
configure vendored
View File

@@ -116,7 +116,6 @@ For better control, use the options below.
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-nts Disable NTS support
--disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support
--disable-refclock Disable reference clock support
--disable-phc Disable PHC refclock driver
--disable-pps Disable PPS refclock driver
@@ -126,7 +125,6 @@ For better control, use the options below.
--without-libcap Don't use libcap even if it is available
--enable-scfilter Enable support for system call filtering
--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
@@ -135,6 +133,7 @@ For better control, use the options below.
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-chronyc-user=USER Specify default chronyc user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
@@ -219,7 +218,6 @@ EXTRA_CLI_OBJECTS=""
feat_debug=0
feat_cmdmon=1
feat_ntp=1
feat_refclock=1
feat_readline=1
try_editline=1
@@ -243,7 +241,6 @@ try_phc=0
feat_pps=1
try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_aes_gcm_siv=1
try_clock_gettime=1
@@ -253,8 +250,8 @@ feat_timestamping=1
try_timestamping=0
feat_ntp_signd=0
ntp_era_split=""
use_pthread=0
default_user="root"
default_chronyc_user="root"
default_hwclockfile=""
default_pidfile="/var/run/chrony/chronyd.pid"
default_rtcdevice="/dev/rtc"
@@ -308,9 +305,6 @@ do
--disable-cmdmon)
feat_cmdmon=0
;;
--disable-ntp)
feat_ntp=0
;;
--disable-refclock)
feat_refclock=0
;;
@@ -341,9 +335,6 @@ do
--without-seccomp)
try_seccomp=0
;;
--disable-asyncdns)
feat_asyncdns=0
;;
--disable-forcednsretry)
feat_forcednsretry=0
;;
@@ -365,6 +356,9 @@ do
--with-user=* )
default_user=`echo $option | sed -e 's/^.*=//;'`
;;
--with-chronyc-user=* )
default_chronyc_user=`echo $option | sed -e 's/^.*=//;'`
;;
--with-hwclockfile=* )
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -504,27 +498,15 @@ if [ $feat_cmdmon = "1" ]; then
EXTRA_OBJECTS="$EXTRA_OBJECTS cmdmon.o manual.o pktlength.o"
fi
if [ $feat_ntp = "1" ]; then
add_def FEAT_NTP
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o"
if [ $feat_ntp_signd = "1" ]; then
add_def FEAT_SIGND
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
fi
else
feat_asyncdns=0
feat_timestamping=0
fi
if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then
EXTRA_OBJECTS="$EXTRA_OBJECTS addrfilt.o clientlog.o keys.o nameserv.o"
else
feat_ipv6=0
if [ $feat_ntp_signd = "1" ]; then
add_def FEAT_SIGND
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
fi
if [ $feat_refclock = "1" ]; then
add_def FEAT_REFCLOCK
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o \
refclock_rtc.o refclock_shm.o refclock_sock.o"
fi
MYCC="$CC"
@@ -684,13 +666,15 @@ then
fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, (void *)1);'
if test_code 'clock_gettime()' 'time.h' '' '' '
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, (void *)1);'
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' '
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
@@ -705,25 +689,28 @@ then
exit 1
fi
if [ $feat_asyncdns = "1" ] && \
test_code 'pthread' 'pthread.h' '-pthread' '' '
pthread_t thread;
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
if test_code 'pthread' 'pthread.h' '-pthread' '' '
pthread_t thread;
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
then
add_def FEAT_ASYNCDNS
add_def USE_PTHREAD_ASYNCDNS
EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
use_pthread=1
MYCFLAGS="$MYCFLAGS -pthread"
else
echo "error: pthread_create() not found"
exit 1
fi
if [ $try_arc4random = "1" ] && \
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
'arc4random_buf((void *)1, 1);'
test_code 'arc4random_buf()' 'stdlib.h' '' '' '
char c;
arc4random_buf(&c, 1);'
then
add_def HAVE_ARC4RANDOM
else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom((void *)1, 1, 0);'; then
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' '
char c;
return getrandom(&c, 1, 0);'
then
add_def HAVE_GETRANDOM
fi
fi
@@ -816,13 +803,12 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
'seccomp_init(SCMP_ACT_KILL);'
then
add_def FEAT_SCFILTER
if [ $feat_ntp = "1" ]; then
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
# used by the main thread as the helper process works on one request at
# a time and the async resolver would block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
fi
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
# used by the main thread as the helper process works on one request at
# a time and the async resolver would block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
EXTRA_OBJECTS="$EXTRA_OBJECTS sys_linux_scmp.o"
fi
if [ "x$priv_ops" != "x" ]; then
@@ -860,7 +846,6 @@ if [ $try_setsched = "1" ] && \
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
then
add_def HAVE_PTHREAD_SETSCHEDPARAM
use_pthread=1
fi
if [ $try_lockmem = "1" ] && \
@@ -914,6 +899,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
HASH_OBJ="hash_nettle.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def HAVE_NETTLE
add_def FEAT_SECHASH
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
@@ -923,6 +909,13 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_nettle.o"
fi
if test_code 'nettle_memeql_sec()' 'nettle/memops.h' \
"$test_cflags" "$test_link" \
'return nettle_memeql_sec("", "", 0);'
then
add_def HAVE_NETTLE_MEMEQL
fi
fi
fi
@@ -936,6 +929,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def HAVE_GNUTLS
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
@@ -977,7 +971,7 @@ EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
if [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
test_cflags=""
test_link=""
@@ -997,7 +991,6 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
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,
@@ -1025,18 +1018,11 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
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((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
fi
fi
fi
if grep '#define HAVE_SIV' config.h > /dev/null; then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o tls_gnutls.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
@@ -1045,10 +1031,6 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
fi
fi
if [ $use_pthread = "1" ]; then
MYCFLAGS="$MYCFLAGS -pthread"
fi
SYSCONFDIR=/etc
if [ "x$SETSYSCONFDIR" != "x" ]; then
SYSCONFDIR=$SETSYSCONFDIR
@@ -1109,12 +1091,13 @@ add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
add_def DEFAULT_PID_FILE "\"$default_pidfile\""
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_CHRONYC_USER "\"$default_chronyc_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features SECHASH IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS NTS`"
chronyd_features="`get_features CMDMON REFCLOCK RTC PRIVDROP SCFILTER SIGND NTS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
@@ -1152,6 +1135,7 @@ do
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
s%@DEFAULT_USER@%${default_user}%;\
s%@DEFAULT_CHRONYC_USER@%${default_chronyc_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f
done

View File

@@ -17,6 +17,7 @@ CHRONYRUNDIR = @CHRONYRUNDIR@
CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@
DEFAULT_CHRONYC_USER = @DEFAULT_CHRONYC_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
@@ -29,6 +30,7 @@ SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
s%\@DEFAULT_CHRONYC_USER\@%$(DEFAULT_CHRONYC_USER)%g;\
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"

View File

@@ -3,7 +3,8 @@
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2024
// Copyright (C) Paul Donald 2025
// Copyright (C) Miroslav Lichvar 2009-2025
//
// 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
@@ -69,12 +70,21 @@ 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.
response to the last 8 requests), unsynchronised, a falseticker (i.e. does not
agree with a majority of other sources), the root distance is too large (the
limit can be configured by the <<maxdistance,*maxdistance*>> directive), or
the stratum is too small or too large (the limits can be configured by the
<<minstratum,*minstratum*>> and <<maxstratum,*maxstratum*>> directives). The
automatic replacement happens at most once per 30 minutes and only one
server can be replaced at a time. The address is also refreshed periodically
when the server is working normally (the interval can be configured by the
<<refresh,*refresh*>> directive).
+
This directive can be used multiple times to specify multiple servers.
This directive can be used multiple times to specify multiple servers. Each
server must have a different IP address to be usable by *chronyd*. If a server
is specified by a hostname that resolves only to addresses already used by
other servers, the server will be treated as having an unknown address (e.g.
reported by the *chronyc* <<chronyc.adoc#activity,*activity*>> command).
+
The directive supports the following options:
+
@@ -108,13 +118,13 @@ the current interval needs to be at least two times longer than the minimum
interval in order to allow a burst with two requests.
*key* _ID_:::
The NTP protocol supports a message authentication code (MAC) to prevent
computers having their system time upset by rogue packets being sent to them.
computers from having their system time upset by rogue packets being sent to them.
The MAC is generated as a function of a key specified in the key file,
which is specified by the <<keyfile,*keyfile*>> directive.
+
The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
should *chronyd* use to authenticate requests sent to the server and verify its
responses. The server must have the same key for this number configured,
*chronyd* should use to authenticate requests sent to the server and verify its
responses. The server must have the same key for this number configured;
otherwise no relationship between the computers will be possible.
+
If the server is running *ntpd* and the output size of the hash function used
@@ -128,7 +138,7 @@ the Transport Layer Security (TLS) protocol to get the keys and cookies
required by NTS for authentication of NTP packets.
+
With this option, the hostname specified in the server or pool directive is the
NTS-KE server or pool of NTS-KE servers respectively. The NTP server usually
NTS-KE server or pool of NTS-KE servers, respectively. The NTP server usually
runs on the same host, but it can be separated from the NTS-KE server (the
hostname or address of the NTP server is provided to the client by the NTS-KE
server).
@@ -145,10 +155,10 @@ default trusted certificate authorities.
*chronyd* uses the network round-trip delay to the server to determine how
accurate a particular measurement is likely to be. Long round-trip delays
indicate that the request, or the response, or both were delayed. If only one
of the messages was delayed the measurement error is likely to be substantial.
of the messages was delayed, the measurement error is likely to be substantial.
+
For small variations in the round-trip delay, *chronyd* uses a weighting scheme
when processing the measurements. However, beyond a certain level of delay the
when processing the measurements. Beyond a certain level of delay, however, the
measurements are likely to be so corrupted as to be useless. (This is
particularly so on wireless networks and other slow links, where a long delay
probably indicates a highly asymmetric delay caused by the response waiting
@@ -158,7 +168,7 @@ 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 greater than 0.3 seconds should be ignored. The default value
is 3 seconds and the maximum value is 1000 seconds.
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
@@ -198,7 +208,7 @@ The asymmetry can be between -0.5 and +0.5. A negative value means the delay of
packets sent to the source is more variable than the delay of packets sent from
the source back. By default, *chronyd* estimates the asymmetry automatically.
*offset* _offset_:::
This option specifies a correction (in seconds) which will be applied to
This option specifies a correction (in seconds) that will be applied to
offsets measured with this source. It's particularly useful to compensate for a
known asymmetry in network delay or timestamping errors. For example, if
packets sent to the source were on average delayed by 100 microseconds more
@@ -210,6 +220,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.
*maxunreach* _polls_:::
This option specifies the maximum number of polls that this source can stay
selected for synchronisation when it is unreachable (i.e. no valid response was
received to the last 8 requests). Only sources with at least one sample more
recent than the oldest sample of all reachable sources can be selected. The
default is 100000.
*filter* _polls_:::
This option enables a median filter to reduce noise in NTP measurements. The
filter will process samples collected in the specified number of polls
@@ -244,11 +260,11 @@ authenticated source to be safely combined with unauthenticated sources in
order to improve the accuracy of the clock. They can be selected and used for
synchronisation only if they agree with the trusted and required source.
*xleave*:::
This option enables the interleaved mode of NTP. It enables the server to
respond with more accurate transmit timestamps (e.g. kernel or hardware
timestamps), which cannot be contained in the transmitted packet itself and
need to refer to a previous packet instead. This can significantly improve the
accuracy and stability of the measurements.
This option enables the interleaved mode of NTP (RFC 9769). It enables the
server to respond with more accurate transmit timestamps (e.g. kernel or
hardware timestamps), which cannot be contained in the transmitted packet
itself and need to refer to a previous packet instead. This can significantly
improve the accuracy and stability of the measurements.
+
The interleaved mode is compatible with servers that support only the basic
mode. Note that even
@@ -292,7 +308,7 @@ included in a *server* directive:
presend 9
----
+
when the polling interval is 512 seconds or more, an extra NTP client packet
When the polling interval is 512 seconds or more, an extra NTP client packet
will be sent to the server a short time (2 seconds) before making the actual
measurement.
+
@@ -303,15 +319,16 @@ When the synchronisation source is selected from available sources, sources
with lower stratum are normally slightly preferred. This option can be used to
increase stratum of the source to the specified minimum, so *chronyd* will
avoid selecting that source. This is useful with low-stratum sources that are
known to be unreliable or inaccurate and which should be used only when other
sources are unreachable.
known to be unreliable or inaccurate and that should be used only when other
sources are unreachable. Note that the <<minstratum,*minstratum*>> directive
is distinct from this option.
*version* _version_:::
This option sets the NTP version of packets sent to the server. This can be
useful when the server runs an old NTP implementation that does not respond to
requests using a newer version. The default version depends on other options.
If the *extfield* or *xleave* option is used, the default version is 4. If
those options are not used and the *key* option specifies a key using a hash
function with output size longer than 160 bits (e.g. SHA256), the default
function with an output size longer than 160 bits (e.g. SHA256), the default
version is 3 for compatibility with older *chronyd* servers. In other cases,
the default version is 4.
*copy*:::
@@ -374,7 +391,7 @@ directive too. There is one option specific to the *pool* directive:
*maxsources* _sources_:::
This option sets the desired number of sources to be used from the pool.
*chronyd* will repeatedly try to resolve the name until it gets this number of
sources responding to requests. The default value is 4 and the maximum value is
sources responding to requests. The default value is 4, and the maximum value is
16.
+
An example of the *pool* directive is
@@ -398,7 +415,7 @@ The following options of the *server* directive do not work in the *peer*
directive: *iburst*, *burst*, *nts*, *presend*, *copy*.
+
When using the *xleave* option, both peers must support and have enabled the
interleaved mode, otherwise the synchronisation will work in one direction
interleaved mode; otherwise the synchronisation will work in one direction
only.
When a key is specified by the *key* option to enable authentication, both
peers must use the same key and the same key number.
@@ -426,7 +443,7 @@ recommended to use two separate client/server associations (specified by the
directive.)
+
The purpose of the *initstepslew* directive is to allow *chronyd* to make a
rapid measurement of the system clock error at boot time, and to correct the
rapid measurement of the system clock error at boot time and to correct the
system clock by stepping before normal operation begins. Since this would
normally be performed only at an appropriate point in the system boot sequence,
no other software should be adversely affected by the step.
@@ -455,7 +472,7 @@ The *initstepslew* directive can also be used in an isolated LAN environment,
where the clocks are set manually. The most stable computer is chosen as the
primary server and the other computers are its clients. If each of the clients
is configured with the <<local,*local*>> directive, the server can be set up
with an *initstepslew* directive which references some or all of the clients.
with an *initstepslew* directive that references some or all of the clients.
Then, if the server machine has to be rebooted, the clients can be relied on to
act analogously to a flywheel and preserve the time for a short period while
the server completes its reboot.
@@ -478,7 +495,7 @@ the driver-specific parameter using the *:* character.
+
This directive can be used multiple times to specify multiple reference clocks.
+
There are four drivers included in *chronyd*:
There are five drivers included in *chronyd*:
+
*PPS*:::
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
@@ -508,7 +525,7 @@ 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
An application that 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
@@ -557,8 +574,9 @@ refclock SHM 1:perm=0644 refid GPS2
----
+
*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
PTP hardware clock (PHC) driver. The parameter is the absolute path to the
device of the PTP clock which should be used as a time source, or the name of
the network interface whose PTP clock should be used. If the clock is kept in
TAI instead of UTC (e.g. it is synchronised by a PTP daemon), the current
UTC-TAI offset needs to be specified by the *offset* option. Alternatively, the
*pps* refclock option can be enabled to treat the PHC as a PPS refclock, using
@@ -596,6 +614,23 @@ refclock PHC /dev/ptp1:nocrossts poll 3 pps
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
----
+
*RTC*:::
Driver for using the Real Time Clock (RTC) as a reference clock. The parameter
is the path to the RTC character device which should be used as a time source.
This driver cannot be used together with the <<rtcfile,*rtcfile*>> or
<<rtcsync,*rtcsync*>> directive. The driver supports the following option:
+
*utc*::::
Assume that RTC keeps Universal Coordinated Time (UTC) instead of local
time.
{blank}:::
+
Examples:
+
----
refclock RTC /dev/rtc0:utc
----
+
{blank}::
The *refclock* directive supports the following options:
+
@@ -622,18 +657,18 @@ specified by its reference ID. In this mode received PPS samples are paired
directly with raw samples from the specified refclock.
*rate* _rate_:::
This option sets the rate of the pulses in the PPS signal (in Hz). This option
controls how the pulses will be completed with real time. To actually receive
controls how the pulses will be completed with real time. In order to receive
more than one pulse per second, a negative *dpoll* has to be specified (-3 for
a 5Hz signal). The default is 1.
*maxlockage* _pulses_:::
This option specifies in number of pulses how old can be samples from the
This option specifies in number of pulses how old samples can be from the
refclock specified by the *lock* option to be paired with the pulses.
Increasing this value is useful when the samples are produced at a lower rate
than the pulses. The default is 2.
*width* _width_:::
This option specifies the width of the pulses (in seconds). It is used to
filter PPS samples when the driver provides samples for both rising and falling
edges. Note that it reduces the maximum allowed error of the time source which
edges. Note that it reduces the maximum allowed error of the time source that
completes the PPS samples. If the duty cycle is configurable, 50% should be
preferred in order to maximise the allowed error.
*pps*:::
@@ -647,7 +682,7 @@ offset (in seconds) is applied to all samples produced by the reference clock.
The default is 0.0.
*delay* _delay_:::
This option sets the NTP delay of the source (in seconds). Half of this value
is included in the maximum assumed error which is used in the source selection
is included in the maximum assumed error, which is used in the source selection
algorithm. Increasing the delay is useful to avoid having no majority in the
source selection or to make it prefer other sources. The default is 1e-9 (1
nanosecond).
@@ -661,14 +696,13 @@ value is the estimated precision of the system clock.
Maximum allowed dispersion for filtered samples (in seconds). Samples with
larger estimated dispersion are ignored. By default, this limit is disabled.
*filter* _samples_:::
This option sets the length of the median filter which is used to reduce the
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. 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_).
This option sets the maximum number of samples that can be stored in the median
filter between polls of the source. The filter combines about 60 percent of the
samples closest to the median offset into one sample. If more samples are
received by the driver between polls, the oldest samples will be dropped. One
sample per poll is sufficient for the source to be selectable. The default
is 64. Note that the PHC driver has additional filtering based on the reading
delay.
*prefer*:::
Prefer this source over other selectable sources without the *prefer* option.
*noselect*:::
@@ -706,6 +740,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.
*maxunreach* _polls_:::
This option specifies the maximum number of polls that this source can stay
selected for synchronisation when it is unreachable (i.e. no valid sample was
received in the last 8 polls). Only sources with at least one sample more
recent than the oldest sample of all reachable sources can be selected.
The default is 100000.
[[manual]]*manual*::
The *manual* directive enables support at run-time for the
@@ -846,7 +886,7 @@ The following IDs are supported:
* 30: AES-128-GCM-SIV
{blank}::
+
The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
The default list of IDs is _30 15_. AES-128-GCM-SIV is preferred over
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
reliability of NTS in networks that block or limit rate of longer NTP messages.
+
@@ -889,7 +929,7 @@ 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 trusted certificates
(in the PEM format) which are needed to verify certificates of NTS-KE servers,
(in the PEM format) that are needed to verify certificates of NTS-KE servers,
e.g. certificates of trusted certificate authorities (CA) or self-signed
certificates of the servers.
+
@@ -924,7 +964,7 @@ specified by the *ntstrustedcerts* directive will be trusted.
[[nocerttimecheck]]*nocerttimecheck* _limit_::
This directive disables the checks of the activation and expiration times of
certificates for the specified number of clock updates. It allows the NTS
authentication mechanism to be used on computers which start with wrong time
authentication mechanism to be used on computers which start with the wrong time
(e.g. due to not having an RTC or backup battery). Disabling the time checks
has important security implications and should be used only as a last resort,
preferably with a minimal number of trusted certificates. The default value is
@@ -938,17 +978,18 @@ nocerttimecheck 1
+
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.
with the 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
This directive specifies the minimum 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.
Only one source is refreshed at a time and only when a valid response is
received (unreachable sources are replaced independently). 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.
@@ -961,11 +1002,11 @@ authentication to limit the impact of man-in-the-middle attacks. The
attackers can drop or delay NTP packets (up to the *maxdelay* and
<<maxdistance,*maxdistance*>> limits), but they cannot modify the timestamps
contained in the packets. The attack can cause only a limited slew or step, and
also cause the clock to run faster or slower than real time (up to double of
also cause the clock to run faster or slower than real time (up to double
the <<maxdrift,*maxdrift*>> limit).
+
When authentication is enabled for an NTP source, it is important to disable
unauthenticated NTP sources which could be exploited in the attack, e.g. if
unauthenticated NTP sources that could be exploited in the attack, e.g. if
they are not reachable only over a trusted network. Alternatively, the source
selection can be configured with the *require* and *trust* options to
synchronise to the unauthenticated sources only if they agree with the
@@ -1041,7 +1082,7 @@ If the selected source was specified with the *prefer* option, it can be
combined only with other sources specified with this option.
+
By default, the limit is 3. Setting the limit to 0 effectively disables the
source combining algorithm and only the selected source will be used to control
source-combining algorithm and only the selected source will be used to control
the system clock.
[[maxdistance]]*maxdistance* _distance_::
@@ -1064,20 +1105,30 @@ with sources that have a small root distance, but their time is too variable.
+
By default, the maximum jitter is 1 second.
[[maxstratum]]*maxstratum* _stratum_::
The *maxstratum* directive sets the maximum allowed stratum of the sources to
be selected for synchronisation. The default value is 15. The useful range is 0
to 15.
[[minstratum]]*minstratum* _stratum_::
The *minstratum* directive sets the minimum allowed stratum of the sources to
be selected for synchronisation. The default value is 0. The useful range is 0
to 15.
[[minsources]]*minsources* _sources_::
The *minsources* directive sets the minimum number of sources that need to be
considered as selectable in the source selection algorithm before the local
clock is updated. The default value is 1.
+
Setting this option to a larger number can be used to improve the reliability.
More sources will have to agree with each other and the clock will not be
More sources will have to agree with each other, and the clock will not be
updated when only one source (which could be serving incorrect time) is
reachable.
[[reselectdist]]*reselectdist* _distance_::
When *chronyd* selects a synchronisation source from available sources, it
will prefer the one with the shortest synchronisation distance. However, to
avoid frequent reselecting when there are sources with similar distance, a
will prefer the one with the shortest synchronisation distance. To avoid
frequent reselecting when there are sources with similar distance, however, a
fixed distance is added to the distance for sources that are currently not
selected. This can be set with the *reselectdist* directive. By default, the
distance is 100 microseconds.
@@ -1095,23 +1146,29 @@ distances are in milliseconds.
[[clockprecision]]*clockprecision* _precision_::
The *clockprecision* directive specifies the precision of the system clock (in
seconds). It is used by *chronyd* to estimate the minimum noise in NTP
measurements and randomise low-order bits of timestamps in NTP responses. By
default, the precision is measured on start as the minimum time to read the
clock.
seconds). This value is used by *chronyd* as the minimum expected error and
amount of noise in NTP and refclock measurements, and to randomise low-order
bits of timestamps in NTP responses to make them less predictable. The minimum
value is 1 nanosecond and the maximum value is 1 second.
+
The measured value works well in most cases. However, it generally
overestimates the precision and it can be sensitive to the CPU speed, which can
change over time to save power. In some cases with a high-precision clocksource
(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the
precision on the server to a smaller value can improve stability of clients'
NTP measurements. The server's precision is reported on clients by the
By default, *chronyd* tries to determine the precision on start-up as the
resolution of the clock. On Linux, it tries to measure the resolution by
observing the minimum change in differences between consecutive readings of the
clock. On other systems it relies on the *clock_getres(2)* system function.
+
If the measurement fails, or the value provided by the system is too large, the
minimum measured time needed to read the clock will be used instead. This value
is typically larger than the resolution, and it is sensitive to the CPU speed,
however, which can change over time to save power.
+
The server's precision is reported on clients by the
<<chronyc.adoc#ntpdata,*ntpdata*>> command.
+
An example setting the precision to 8 nanoseconds is:
An example setting the precision to 1 nanosecond (e.g. when the system clock is
using a Time Stamp Counter (TSC) updated at a rate of at least 1 GHz) is:
+
----
clockprecision 8e-9
clockprecision 1e-9
----
[[corrtimeratio]]*corrtimeratio* _ratio_::
@@ -1124,7 +1181,7 @@ The *corrtimeratio* directive sets the ratio between the duration in which the
clock is slewed for an average correction according to the source history and
the interval in which the corrections are done (usually the NTP polling
interval). Corrections larger than the average take less time and smaller
corrections take more time, the amount of the correction and the correction
corrections take more time; the amount of the correction and the correction
time are inversely proportional.
+
Increasing *corrtimeratio* improves the overall frequency error of the system
@@ -1137,7 +1194,7 @@ The maximum allowed slew rate can be set by the <<maxslewrate,*maxslewrate*>>
directive. The current remaining correction is shown in the
<<chronyc.adoc#tracking,*tracking*>> report as the *System time* value.
[[driftfile]]*driftfile* _file_::
[[driftfile]]*driftfile* _file_ [*interval* _interval_]::
One of the main activities of the *chronyd* program is to work out the rate at
which the system clock gains or loses time relative to real time.
+
@@ -1156,6 +1213,10 @@ microseconds in reality (so the true time has only advanced by 999900
microseconds). The second is an estimate of the error bound around the first
value in which the true rate actually lies.
+
The *interval* option specifies the minimum interval between updates of the
file in seconds. The file is written only on an update of the local clock.
The default interval is 3600 seconds.
+
An example of the driftfile directive is:
+
----
@@ -1179,7 +1240,7 @@ fallbackdrift 16 19
+
In this example, the minimum interval is 16 (18 hours) and the maximum interval is
19 (6 days). The system clock frequency will be set to the first fallback 18
hours after last clock update, to the second after 36 hours, and so on. This
hours after the last clock update, to the second after 36 hours, and so on. This
might be a good setting to cover frequency changes due to daily and weekly
temperature fluctuations. When the frequency is set to a fallback, the state of
the clock will change to '`Not synchronised`'.
@@ -1238,7 +1299,7 @@ When smearing a leap second, the leap status is suppressed on the server and
the served time is corrected slowly by slewing instead of stepping. The clients
do not need any special configuration as they do not know there is any leap
second and they follow the server time which eventually brings them back to
UTC. Care must be taken to ensure they use only NTP servers which smear the
UTC. Care must be taken to ensure they use only NTP servers that smear the
leap second in exactly the same way for synchronisation.
+
This feature must be used carefully, because the server is intentionally not
@@ -1271,15 +1332,15 @@ duration = sqrt(4 / wander)
[[leapsectz]]*leapsectz* _timezone_::
This directive specifies a timezone in the system timezone database which
*chronyd* can use to determine when will the next leap second occur and what is
the current offset between TAI and UTC. It will periodically check if 23:59:59
*chronyd* can use to determine when the next leap second occurs and what
the current offset between TAI and UTC is. It will periodically check if 23:59:59
and 23:59:60 are valid times in the timezone. This normally works with the
_right/UTC_ timezone.
+
When a leap second is announced, the timezone needs to be updated at least 12
hours before the leap second. It is not necessary to restart *chronyd*.
+
This directive is useful with reference clocks and other time sources which do
This directive is useful with reference clocks and other time sources that do
not announce leap seconds, or announce them too late for an NTP server to
forward them to its own clients. Clients of leap smearing servers must not
use this directive.
@@ -1321,7 +1382,7 @@ leapseclist /usr/share/zoneinfo/leap-seconds.list
----
[[makestep]]*makestep* _threshold_ _limit_::
Normally *chronyd* will cause the system to gradually correct any time offset,
Normally *chronyd* will cause the system to gradually correct any time offset
by slowing down or speeding up the clock as required. In certain situations,
e.g. when *chronyd* is initially started, the system clock might be so far
adrift that this slewing process would take a very long time to correct the
@@ -1343,6 +1404,9 @@ makestep 0.1 3
+
This would step the system clock if the adjustment is larger than 0.1 seconds, but
only in the first three clock updates.
+
Note that this directive does not work and should not be used when the system
clock control is disabled by the *-x* option of *chronyd*.
[[maxchange]]*maxchange* _offset_ _start_ _ignore_::
This directive sets the maximum offset to be accepted on a clock update. The
@@ -1550,13 +1614,13 @@ allow all 1.2.0.0/16
+
In the first example, the effect is the same regardless of what order the three
directives are given in. So the _1.2.0.0/16_ subnet is allowed access, except
for the _1.2.3.0/24_ subnet, which is denied access, however the host _1.2.3.4_
for the _1.2.3.0/24_ subnet, which is denied access, while the host _1.2.3.4_
is allowed access.
+
In the second example, the *allow all 1.2.0.0/16* directive overrides the
effect of _any_ previous directive relating to a subnet within the specified
subnet. Within a configuration file this capability is probably rather moot;
however, it is of greater use for reconfiguration at run-time via *chronyc*
yet, it is of greater use for reconfiguration at run-time via *chronyc*
with the <<chronyc.adoc#allow,*allow all*>> command.
+
The rules are internally represented as a tree of tables with one level per
@@ -1583,7 +1647,7 @@ This is similar to the <<allow,*allow*>> directive, except that it denies NTP
and NTS-KE client access to a particular subnet or host, rather than allowing
it.
+
The syntax is identical and the directive can be used multiple times too.
The syntax is identical, and the directive can be used multiple times too.
+
There is also a *deny all* directive with similar behaviour to the *allow all*
directive.
@@ -1601,7 +1665,7 @@ bindaddress 192.168.1.1
----
+
Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
directive can be specified. Therefore, it is not useful on computers which
directive can be specified. Therefore, it is not useful on computers that
should serve NTP on multiple network interfaces.
[[binddevice]]*binddevice* _interface_::
@@ -1695,16 +1759,19 @@ expected stratum in the network when external NTP servers are accessible.
+
Stratum 1 indicates a computer that has a true real-time reference directly
connected to it (e.g. GPS, atomic clock, etc.), such computers are expected to
be very close to real time. Stratum 2 computers are those which have a stratum
be very close to real time. Stratum 2 computers are those that have a stratum
1 server; stratum 3 computers have a stratum 2 server and so on. A value
of 10 indicates that the clock is so many hops away from a reference clock that
its time is fairly unreliable.
*distance* _distance_:::
This option sets the threshold for the root distance which will activate the local
reference. If *chronyd* was synchronised to some source, the local reference
will not be activated until its root distance reaches the specified value (the
rate at which the distance is increasing depends on how well the clock was
tracking the source). The default value is 1 second.
reference. If *chronyd* was synchronised to a configured time source, the local
reference will not be activated until its root distance reaches the specified
value (the rate at which the distance is increasing depends on how well the
clock was tracking the source). When the clock is not synchronised, it is
considered to have an infinite root distance, i.e. the local reference
activates as soon as allowed by the *waitunsynced* option. The default
threshold is 1 second.
+
The current root distance can be calculated from root delay and root dispersion
(reported by the <<chronyc.adoc#tracking,*tracking*>> command in *chronyc*) as:
@@ -1727,7 +1794,7 @@ unless no other source is selectable and their reference IDs are smaller than
the local reference ID.
+
This allows multiple servers in the network to use the same *local*
configuration and to be synchronised to one another, without confusing clients
configuration and to be synchronised to one another without confusing clients
that poll more than one server. Each server needs to be configured to poll all
other servers with the *local* directive. This ensures only the server with the
smallest reference ID has the local reference active and others are
@@ -1737,12 +1804,30 @@ smallest reference ID will take over when its local reference mode activates
+
The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
*tos orphan* command).
*waitsynced* _interval_:::
This option specifies the minimum interval (in seconds) between the last update
of the clock and activation of the local reference as configured by the
*distance* and *activate* options. The *distance* option can be set to 0 to
ignore the root distance and control the activation only by the interval. In
such case it should be at least as long as the maximum expected polling
interval to prevent frequent activation in normal polling of the source.
The default minimum interval is 0.
*waitunsynced* _interval_:::
This option specifies how long (in seconds) *chronyd* needs to wait before
activating the local reference when the clock is not considered to be
synchronised (e.g. after start or the source selection failing due to no
majority). This delay prevents *chronyd* from serving incorrect time to clients
before the configured time sources are given a chance to synchronise the local
clock. The default interval is 300 seconds if the *orphan* option is set,
otherwise it is 0 (i.e. local reference activates immediately).
{blank}::
+
An example of the directive is:
Examples of the directive are:
+
----
local stratum 5
local stratum 10 orphan distance 0.1 activate 0.5
local stratum 10 orphan distance 0.0 waitsynced 7200 waitunsynced 300
----
[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
@@ -1791,7 +1876,7 @@ the user under which *chronyd* is running after dropping root privileges. For
security reasons, it should not be readable by other users.
+
This directive can be used multiple times to specify multiple keys. The number
of keys must be the same as the number of certificates and the corresponding
of keys must be the same as the number of certificates, and the corresponding
files must be specified in the same order.
[[ntsprocesses]]*ntsprocesses* _processes_::
@@ -1799,7 +1884,7 @@ This directive specifies how many helper processes will *chronyd* operating
as an NTS server start for handling client NTS-KE requests in order to improve
performance with multi-core CPUs and multithreading. If set to 0, no helper
process will be started and all NTS-KE requests will be handled by the main
*chronyd* process. The default value is 1.
*chronyd* process. The default value is 1, and the maximum value is 1000.
[[maxntsconnections]]*maxntsconnections* _connections_::
This directive specifies the maximum number of concurrent NTS-KE connections
@@ -1820,7 +1905,7 @@ The following IDs are supported:
* 30: AES-128-GCM-SIV
{blank}::
+
The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
The default list of IDs is _30 15_. AES-128-GCM-SIV is preferred over
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
reliability of NTS in networks that block or limit rate of longer NTP messages.
+
@@ -1833,7 +1918,7 @@ ntsaeads 15
This list is used also by the <<ntsaeads1,NTS client>>.
+
Note the the NTS specification (RFC 8915) requires servers to support
AES-SIV-CMAC-256, i.e. 15 should be always included in the specified list.
AES-SIV-CMAC-256, i.e. 15 should always be included in the specified list.
+
The AES-128-GCM-SIV keys used by *chronyd* do not comply to RFC 8915 for
compatibility with older *chrony* clients unless the use of compliant keys is
@@ -1841,7 +1926,7 @@ negotiated with an
https://chrony-project.org/doc/spec/nts-compliant-128gcm.html[NTS-KE record].
Support for this record was added in version 4.6.1. As a client, *chronyd* can
interoperate with a server that uses compliant keys, but does not support the
negotiation, if it responds to incorrectly authenticated requests with an NTS
negotiation if it responds to incorrectly authenticated requests with an NTS
NAK.
[[ntsdumpdir2]]*ntsdumpdir* _directory_::
@@ -1861,9 +1946,9 @@ This directory is used also by the <<ntsdumpdir1,NTS client>> to save NTS cookie
[[ntsntpserver]]*ntsntpserver* _hostname_::
This directive specifies the hostname (as a fully qualified domain name) or
address of the NTP server(s) which is
address of the NTP server(s) that is
provided in the NTS-KE response to the clients. It allows the NTS-KE server to
be separated from the NTP server. However, the servers need to share the keys,
be separated from the NTP server. The servers need to share the keys, however,
i.e. external key management needs to be enabled by setting
<<ntsrotate,*ntsrotate*>> to 0. By default, no hostname or address is provided
to the clients, which means they should use the same server for NTS-KE and NTP.
@@ -1915,7 +2000,7 @@ addresses. If multiple clients share one IP address (e.g. multiple hosts behind
NAT), the sum of their traffic will be limited. If a client that increases its
polling rate when it does not receive a reply is detected, its rate limiting
will be temporarily suspended to avoid increasing the overall amount of
traffic. The maximum number of IP addresses which can be monitored at the same
traffic. The maximum number of IP addresses that can be monitored at the same
time depends on the memory limit set by the <<clientloglimit,*clientloglimit*>>
directive.
+
@@ -1933,7 +2018,7 @@ than the specified interval).
This option sets the maximum number of responses that can be sent in a burst,
temporarily exceeding the limit specified by the *interval* option. This is
useful for clients that make rapid measurements on start (e.g. *chronyd* with
the *iburst* option). The default value is 8. The minimum value is 1 and the
the *iburst* option). The default value is 8. The minimum value is 1, and the
maximum value is 255.
*leak* _rate_:::
This option sets the rate at which responses are randomly allowed even if the
@@ -1941,7 +2026,7 @@ limits specified by the *interval* and *burst* options are exceeded. This is
necessary to prevent an attacker who is sending requests with a spoofed
source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the
least every fourth request has a response. The minimum value is 1, and the
maximum value is 4.
*kod* _rate_:::
This option sets the rate at which Kiss-o'-Death (KoD) RATE responses are
@@ -1949,7 +2034,7 @@ randomly sent when the limits specified by the *interval* and *burst* options
are exceeded. It is an additional stream of responses to the *leak* option. A
KoD RATE response is a request for the client to reduce its polling rate. Few
implementations actually support it. The rate is defined as a power of 1/2. The
default value is 0, which means disabled. The minimum value is 0 and the
default value is 0, which means disabled. The minimum value is 0, and the
maximum value is 4.
{blank}::
+
@@ -1978,13 +2063,13 @@ ntsratelimit interval 3 burst 1
The *smoothtime* directive can be used to enable smoothing of the time that
*chronyd* serves to its clients to make it easier for them to track it and keep
their clocks close together even when large offset or frequency corrections are
applied to the server's clock, for example after being offline for a longer
applied to the server's clock, for example, after being offline for a longer
time.
+
BE WARNED: The server is intentionally not serving its best estimate of the
true time. If a large offset has been accumulated, it can take a very long time
to smooth it out. This directive should be used only when the clients are not
configured to also poll another NTP server, because they could reject this
configured to poll another NTP server also, because they could reject this
server as a falseticker or fail to select a source completely.
+
The smoothing process is implemented with a quadratic spline function with two
@@ -1998,7 +2083,7 @@ reset*>> command.
The first two arguments of the directive are the maximum frequency offset of
the smoothed time to the tracked NTP time (in ppm) and the maximum rate at
which the frequency offset is allowed to change (in ppm per second). *leaponly*
is an optional third argument which enables a mode where only leap seconds are
is an optional third argument that enables a mode where only leap seconds are
smoothed out and normal offset and frequency changes are ignored. The *leaponly*
option is useful in a combination with the <<leapsecmode,*leapsecmode slew*>>
directive to allow the clients to use multiple time smoothing servers safely.
@@ -2082,6 +2167,9 @@ The syntax is identical to the *allow* directive.
There is also a *cmdallow all* directive with similar behaviour to the *allow
all* directive (but applying to monitoring access in this case, of course).
+
Addresses _127.0.0.1_ and _::1_ (i.e. the loopback interface) are always
allowed.
+
Note that *chronyd* has to be configured with the
<<bindcmdaddress,*bindcmdaddress*>> directive to not listen only on the
loopback interface to actually allow remote access.
@@ -2123,6 +2211,27 @@ An example of the use of the directive is:
cmdratelimit interval 2
----
[[opencommands]]*opencommands* [_command_]...::
This directive specifies a list of monitoring commands to be enabled for the
hosts allowed by the *cmdallow* directive. The following commands can be
specified (the naming follows *chronyc*):
+
*activity*+*+, *authdata*, *clients*, *manual*+*+, *ntpdata*, *rtcdata*+*+,
*selectdata*, *serverstats*+*+, *smoothing*+*+, *sourcename*+*+, *sources*+*+,
*sourcestats*, *tracking*+*+.
+
The commands marked with +*+ are enabled by default. The protocol of these
commands is considered stable and can be expected to work between different
versions of *chronyc* and *chronyd*. The protocol of the other commands is not
considered stable and different versions of *chronyc* and *chronyd* may not
interoperate. When that happens, *chronyc* will print an '`Invalid command`' or
'`Bad reply from daemon`' error.
+
Note that some of the reported data can be potentially useful to attackers,
enabling them to observe and predict better the internal state of *chronyd*.
It is recommended to enable only commands that are actually needed for
monitoring and limit the access to the hosts that need it.
=== Real-time clock (RTC)
[[hwclockfile]]*hwclockfile* _file_::
@@ -2188,7 +2297,7 @@ option to *chronyd*) if the following three conditions apply:
[[rtconutc]]*rtconutc*::
*chronyd* assumes by default that the RTC keeps local time (including any
daylight saving changes). This is convenient on PCs running Linux which are
daylight saving changes). This is convenient on PCs running Linux that are
dual-booted with Windows.
+
If you keep the RTC on local time and your computer is off when daylight saving
@@ -2203,7 +2312,8 @@ The directive takes no arguments. It is equivalent to specifying the *-u*
switch to the Linux *hwclock* program.
+
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
and is not relevant for the <<rtcsync,*rtcsync*>> directive.
and is not relevant for the <<rtcsync,*rtcsync*>> directive or when the RTC
is used as reference clock.
[[rtcsync]]*rtcsync*::
The *rtcsync* directive enables a mode where the system time is periodically
@@ -2276,7 +2386,7 @@ from the example line above):
+
*measurements*:::
This option is identical to the *rawmeasurements* option, except it logs only
valid measurements from synchronised sources, i.e. measurements which passed
valid measurements from synchronised sources, i.e. measurements that passed
the RFC 5905 tests 1 through 7. This can be useful for producing graphs of the
source's performance.
+
@@ -2317,7 +2427,7 @@ from the example line above):
used to prune old samples when it no longer looks like the measurements fit a
linear model). [0, i.e. no samples discarded this time]
. The number of runs. The number of runs of regression residuals with the same
sign is computed. If this is too small it indicates that the measurements are
sign is computed. If this is too small, it indicates that the measurements are
no longer represented well by a linear model and that some older samples need
to be discarded. The number of runs for the data that is being retained is
tabulated. Values of approximately half the number of samples are expected.
@@ -2353,6 +2463,9 @@ Not considered selectable for synchronisation:
* _N_ - has the *noselect* option.
* _s_ - is not synchronised.
* _M_ - does not have enough measurements.
* _r_ - has a stratum outside of the allowed range (configured by the
<<minstratum,*minstratum*>> and <<maxstratum,*maxstratum*>>
directives).
* _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
@@ -2536,9 +2649,9 @@ log measurements statistics tracking
A banner is periodically written to the log files enabled by the <<log,*log*>>
directive to indicate the meanings of the columns.
+
The *logbanner* directive specifies after how many entries in the log file
should be the banner written. The default is 32, and 0 can be used to disable
it entirely.
The *logbanner* directive specifies the cadence size of how many entries are
written to the log file after which the banner is written anew. The default is
32, and 0 can be used to disable it entirely.
[[logchange]]*logchange* _threshold_::
This directive sets the threshold for the adjustment of the system clock that
@@ -2628,7 +2741,7 @@ sourcedir /var/run/chrony-dhcp
[[include]]*include* _pattern_::
The *include* directive includes a configuration file, or multiple configuration
files if a wildcard pattern is specified. Unlike with the *confdir* directive,
the full name of the files needs to be specified and at least one file is
the full name of the files needs to be specified, and at least one file is
required to exist.
+
This directive can be used multiple times.
@@ -2714,8 +2827,8 @@ timestamp and the actual reception time at the physical layer. This value will
be subtracted from receive timestamps obtained from the NIC. The default value
is 0.
*nocrossts*:::
Some hardware can precisely cross timestamp the NIC clock with the system
clock. This option disables the use of the cross timestamping.
Some hardware can precisely cross-timestamp the NIC clock with the system
clock. This option disables the use of the cross-timestamping.
*rxfilter* _filter_:::
This option selects the receive timestamping filter. The _filter_ can be one of
the following:
@@ -2755,7 +2868,7 @@ 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
configures how long *chronyd* should 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.
@@ -2770,7 +2883,7 @@ 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
keys, which are shared between NTP servers and clients, or peers, in order to
authenticate NTP packets with a message authentication code (MAC) using a
cryptographic hash function or cipher.
+
@@ -2797,7 +2910,7 @@ Each line consists of an ID, optional type, and key.
+
The ID can be any positive integer in the range 1 through 2^32-1.
+
The type is a name of a cryptographic hash function or cipher which is used to
The type is a name of a cryptographic hash function or cipher that 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
@@ -2856,7 +2969,7 @@ Setting this directive to _/_ disables writing and checking of the PID file.
[[ptpport]]*ptpport* _port_::
The *ptpport* directive enables *chronyd* to send and receive NTP messages
contained in PTP event messages (NTP-over-PTP) to enable hardware timestamping
on NICs which cannot timestamp NTP packets, but can timestamp unicast PTP
on NICs that cannot timestamp NTP packets, but can timestamp unicast PTP
packets, and also use corrections provided by PTP one-step end-to-end
transparent clocks in network switches and routers. The port recognized by the
NICs and PTP transparent clocks is 319 (PTP event port). The default value is 0
@@ -2869,7 +2982,7 @@ The PTP port will be open even if *chronyd* is not configured to operate as a
server or client. The directive does not change the default protocol of
specified NTP sources. Each NTP source that should use NTP-over-PTP needs to
be specified with the *port* option set to the PTP port. To actually enable
hardware timestamping on NICs which can timestamp PTP packets only, the
hardware timestamping on NICs that can timestamp PTP packets only, the
*rxfilter* option of the *hwtimestamp* directive needs to be set to _ptp_. The
extension field _F324_ needs to be enabled to use the corrections provided by
the PTP transparent clocks.
@@ -2926,7 +3039,7 @@ The compiled-in default value is _@DEFAULT_USER@_.
=== NTP client with permanent connection to NTP servers
This section shows how to configure *chronyd* for computers that are connected
to the Internet (or to any network containing true NTP servers which ultimately
to the Internet (or to any network containing true NTP servers that ultimately
derive their time from a reference clock) permanently or most of the time.
To operate in this mode, you will need to know the names of the NTP servers
@@ -2952,7 +3065,7 @@ server ntp2.example.net
server ntp3.example.net
----
However, you will probably want to include some of the other directives. The
You will probably want to include some of the other directives, however. The
<<driftfile,*driftfile*>>, <<makestep,*makestep*>> and <<rtcsync,*rtcsync*>>
might be particularly useful. Also, the *iburst* option of the
<<server,*server*>> directive is useful to speed up the initial
@@ -3041,7 +3154,7 @@ actually connected to the Internet.
=== Isolated networks
This section shows how to configure *chronyd* for computers that never have
network connectivity to any computer which ultimately derives its time from a
network connectivity to any computer that ultimately derives its time from a
reference clock.
In this situation, one computer is selected to be the primary timeserver. The
@@ -3056,7 +3169,7 @@ in the form of the <<manual,*manual*>> directive and the
<<chronyc.adoc#settime,*settime*>> command in the *chronyc* program.
If the server is rebooted, *chronyd* can re-read the drift rate from the drift
file. However, the server has no accurate estimate of the current time. To get
file. The server has no accurate estimate of the current time, however. To get
around this, the system can be configured so that the server can initially set
itself to a '`majority-vote`' of selected clients' times; this allows the
clients to '`flywheel`' the server while it is rebooting.
@@ -3124,7 +3237,7 @@ clients might be configured to poll all three servers.
=== RTC tracking
This section considers a computer which has occasional connections to the
This section considers a computer that has occasional connections to the
Internet and is turned off between '`sessions`'. In this case, *chronyd* relies
on the computer's RTC to maintain the time between the periods when it is
powered up. It assumes that Linux is run exclusively on the computer. Dual-boot
@@ -3135,7 +3248,7 @@ enable the *HPET_EMULATE_RTC* option in your kernel configuration. Otherwise,
using it.
When the computer is connected to the Internet, *chronyd* has access to
external NTP servers which it makes measurements from. These measurements are
external NTP servers that it makes measurements from. These measurements are
saved, and straight-line fits are performed on them to provide an estimate of
the computer's time error and rate of gaining or losing time.
@@ -3178,7 +3291,7 @@ take *chronyd* offline; because *chronyd* free-runs between online sessions, no
parameters will change significantly between going offline from the Internet
and any power failure.
A final point regards computers which are left running for extended periods and
A final point regards computers that are left running for extended periods and
where it is desired to spin down the hard disc when it is not in use (e.g. when
not accessed for 15 minutes). *chronyd* has been planned so it supports such
operation; this is the reason why the RTC tracking parameters are not saved to

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-2024
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2025
//
// 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
@@ -50,7 +50,7 @@ running under a non-root user), it will try to connect to 127.0.0.1 and then
::1.
Only the following monitoring commands, which do not affect the behaviour of
*chronyd*, are allowed from the network: *activity*, *manual list*,
*chronyd*, are allowed from the network by default: *activity*, *manual list*,
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
*waitsync*. The
set of hosts from which *chronyd* will accept these commands can be configured
@@ -58,7 +58,10 @@ with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
default, the commands are accepted only from localhost (127.0.0.1 or ::1).
All other commands are allowed only through the Unix domain socket. When sent
Other monitoring commands can be enabled for network access by the
<<chrony.conf.adoc#opencommands,*opencommands*>> directive. Monitoring commands
with disabled network access and commands that affect the behaviour of
*chronyd* are allowed only through the Unix domain socket. If they are sent
over the network, *chronyd* will respond with a '`Not authorised`' error, even
if it is from localhost.
@@ -118,6 +121,13 @@ This option allows the user to specify the UDP port number which the target
*chronyd* is using for its monitoring connections. This defaults to 323; there
would rarely be a need to change this.
*-u* _user_::
This option sets the name of the user to which *chronyc* will switch when it is
started under the root user in order to drop its privileges. *chronyc* will not
try to change its identity if the option is set to _root_, or the effective
user ID of the started process is not 0. The compiled-in default value is
_@DEFAULT_CHRONYC_USER@_.
*-f* _file_::
This option is ignored and is provided only for compatibility.
@@ -364,9 +374,12 @@ a measurement is being made every 64 seconds. *chronyd* automatically varies
the polling rate in response to prevailing conditions.
*Reach*:::
This shows the source's 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.
register has 8 bits. It is shifted to left by one bit with each poll and it is
updated by 1 when a valid NTP response, or just a sample in case of a reference
clock, is received from the source. A value of 377 indicates that a valid
response or sample was received for all of the last 8 polls. Note that samples
can be dropped if they are not considered good enough for synchronisation, but
the reachability register will still have 1s for their polls.
*LastRx*:::
This column shows how long ago the last good sample (which is shown in the next
column) was received from the source. Measurements that failed some tests are
@@ -461,12 +474,16 @@ synchronisation:
* _N_ - has the *noselect* option.
* _M_ - does not have enough measurements.
* _s_ - is not synchronised.
* _r_ - has a stratum outside of the allowed range (configured by the
<<chrony.conf.adoc#minstratum,*minstratum*>> and
<<chrony.conf.adoc#maxstratum,*maxstratum*>> directives).
* _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
<<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
* _w_ - waits for other sources to get out of the _M_ state.
* _S_ - has older measurements than other sources.
* _S_ - has only measurements older than reachable sources, or is unreachable
for too many polls (configured by the *maxunreach* option).
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
the <<chrony.conf.adoc#local,*local*>> directive).
* _T_ - does not fully agree with sources that have the *trust* option.
@@ -1461,6 +1478,13 @@ purged. An example of how to do this is shown below.
# chronyc cyclelogs
# rm /var/log/chrony/measurements1.log
----
+
Note that log files enabled by the *log* directive are opened when the first
entry is made. The message log file specified by the *chronyd*'s *-l* option is
opened early on start and closed on the *cyclelogs* command only if opening of
the new file succeeds to avoid losing messages. If *chronyd* is configured to
drop root privileges and the directory containing the log file is not writable
for the specified user, *chronyd* will not be able to open the file again.
[[dump]]*dump*::
The *dump* command causes *chronyd* to write its current history of

View File

@@ -217,6 +217,14 @@ client requests until the service is able to handle them. The service manager
sets the LISTEN_FDS environment variable to the number of passed file
descriptors.
*NOTIFY_SOCKET*::
The systemd service manager sets this variable for services of the *notify*
type. *chronyd* sends a message to this socket when it it is fully initialised
and ready to accept commands (e.g. from *chronyc*), with the clock already set
if using the *-s* option or *initstepslew* directive. It is an alternative to
the *forking* service type, which does not need the PID file. *chronyd* needs
to be started with the *-n* or *-d* option to not fork.
== FILES
_@SYSCONFDIR@/chrony.conf_

View File

@@ -165,6 +165,13 @@ versions or implementations of the libraries might make different system calls.
If the filter is missing some system call, `chronyd` could be killed even in
normal operation.
The impact of potential security issues in `chronyc` can be reduced by running
`chronyc` under the _chrony_ user instead of root, or another unprivileged user
if access to the Unix domain socket is not needed. Since version 4.8, `chronyc`
drops root privileges automatically if it is started with the `-u` option
specifying the _chrony_ user, or the name was specified to be the compiled-in
default by the `--with-chronyc-user` option of the configure script.
=== How can I make the system clock more secure?
An NTP client synchronising the system clock to an NTP server is susceptible to
@@ -338,10 +345,10 @@ with local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
If your server supports the interleaved mode (e.g. it is running `chronyd`),
the `xleave` option should be added to the `server` directive to enable the
server to provide the client with more accurate transmit timestamps (kernel or
preferably hardware). For example:
If your server supports the interleaved mode (RFC 9769), e.g. it is running
`chronyd` version 3.0 or later, the `xleave` option should be added to the
`server` directive to enable the server to provide the client with more
accurate transmit timestamps (kernel or preferably hardware). For example:
----
server ntp.local minpoll 2 maxpoll 4 xleave
@@ -897,7 +904,9 @@ measurements from both sources.
If the first source was significantly better than the second source, it can
take many hours before the second source is selected, depending on its polling
interval. You can force a faster reselection by increasing the clock error rate
interval. You can force a faster reselection by reducing the maximum number of
polls the source can still be selected when unreachable (`maxunreach` option
supported since `chrony` version 4.8), increasing the clock error rate
(`maxclockerror` directive), shortening the polling interval (`maxpoll`
option), or reducing the number of samples (`maxsamples` option).

View File

@@ -85,13 +85,6 @@ will be built with support for dropping root privileges. On other systems no
extra library is needed. The default user which `chronyd` should run as can be
specified with the `--with-user` option of the `configure` script.
If development files for the POSIX threads library are available, `chronyd`
will be built with support for asynchronous resolving of hostnames specified in
the `server`, `peer`, and `pool` directives. This allows `chronyd` operating as
a server to respond to client requests when resolving a hostname. If you don't
want to enable the support, specify the `--disable-asyncdns` flag to
`configure`.
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,

View File

@@ -1,4 +1,4 @@
# Use public NTP servers from the pool.ntp.org project.
# Use four public NTP servers from the pool.ntp.org project.
pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.

View File

@@ -1,5 +1,10 @@
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
# Note: The general recommendation for an NTP client is to have at least
# three NTP servers to be able to detect one server providing incorrect
# time (falseticker).
# Use four public NTP servers from the pool.ntp.org project. If this
# host has a static public IP address, please consider joining the pool:
# https://www.ntppool.org/join.html
pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.

View File

@@ -21,10 +21,12 @@
#######################################################################
### SPECIFY YOUR NTP SERVERS
# Most computers using chrony will send measurement requests to one or
# more 'NTP servers'. You will probably find that your Internet Service
# more NTP servers. The general recommendation is to have at least
# three NTP servers to be able to detect one server providing incorrect
# time (falseticker). You will probably find that your Internet Service
# Provider or company have one or more NTP servers that you can specify.
# Failing that, there are a lot of public NTP servers. There is a list
# you can access at http://support.ntp.org/bin/view/Servers/WebHome or
# you can access at https://support.ntp.org/bin/view/Servers/WebHome or
# you can use servers from the pool.ntp.org project.
! server ntp1.example.net iburst

View File

@@ -12,10 +12,11 @@ Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
Type=notify
PIDFile=/run/chrony/chronyd.pid
Environment="OPTIONS="
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd -U $OPTIONS
ExecStart=/usr/sbin/chronyd -n -U $OPTIONS
User=chrony
LogsDirectory=chrony

View File

@@ -6,10 +6,11 @@ Conflicts=ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
Type=notify
PIDFile=/run/chrony/chronyd.pid
Environment="OPTIONS="
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
ExecStart=/usr/sbin/chronyd -n $OPTIONS
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE

View File

@@ -907,6 +907,7 @@ get_date (const char *p, const time_t *now)
yyHour = tmp->tm_hour;
yyMinutes = tmp->tm_min;
yySeconds = tmp->tm_sec;
memset(&tm, 0, sizeof (tm));
tm.tm_isdst = tmp->tm_isdst;
yyMeridian = MER24;
yyRelSeconds = 0;
@@ -943,7 +944,6 @@ get_date (const char *p, const time_t *now)
tm.tm_hour += yyRelHour;
tm.tm_min += yyRelMinutes;
tm.tm_sec += yyRelSeconds;
tm.tm_wday = 0;
/* Let mktime deduce tm_isdst if we have an absolute timestamp,
or if the relative timestamp mentions days, months, or years. */

View File

@@ -49,6 +49,7 @@
#define DELAY_QUANT_MAX_K 2
#define DELAY_QUANT_Q 10
#define DELAY_QUANT_REPEAT 7
#define DELAY_QUANT_LARGE_STEP_DELAY 1000
#define DELAY_QUANT_MIN_STEP 1.0e-9
struct HCL_Instance_Record {
@@ -127,6 +128,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation, doub
clock->precision = precision;
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
DELAY_QUANT_LARGE_STEP_DELAY,
DELAY_QUANT_MIN_STEP);
LCL_AddParameterChangeHandler(handle_slew, clock);
@@ -161,7 +163,8 @@ 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)
struct timespec *hw_ts, struct timespec *local_ts, double *err,
int *quality)
{
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
double delay_sum, hw_sum, local_sum, local_prec, freq;
@@ -199,8 +202,10 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
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 = QNT_GetQuantile(clock->delay_quants, QNT_GetMinK(clock->delay_quants)) -
QNT_GetMinStep(clock->delay_quants) / 2.0;
high_delay = QNT_GetQuantile(clock->delay_quants, QNT_GetMaxK(clock->delay_quants)) +
QNT_GetMinStep(clock->delay_quants) / 2.0;
low_delay = MIN(low_delay, high_delay);
high_delay = MAX(high_delay, low_delay + local_prec);
@@ -224,11 +229,13 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
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);
*quality = 2;
return 1;
}
/* Accept the reading with minimum delay if its interval does not contain
the current offset predicted from previous samples */
/* Indicate acceptable quality of the reading with minimum delay if its
interval does not contain the current offset predicted from previous
samples, or a new sample is needed to get the tracking working */
*hw_ts = tss[min_reading][1];
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
@@ -238,11 +245,14 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
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;
*quality = 1;
} else {
*quality = 0;
}
return 0;
DEBUG_LOG("Min-delay reading err=%e prerr=%e ql=%d", *err, pred_err, *quality);
return 1;
}
/* ================================================== */

View File

@@ -38,10 +38,14 @@ 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 */
/* Process new readings of the HW clock in the form of (sys, hw, sys) triplets
and produce a sample which can be accumulated by HCL_AccumulateSample().
Indicate the quality of the sample relative to already processed samples as
a value of 0, 1, or 2, where a sample of quality 0 should normally be
dropped. */
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err);
struct timespec *hw_ts, struct timespec *local_ts, double *err,
int *quality);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,

5
keys.c
View File

@@ -265,9 +265,6 @@ KEY_Reload(void)
if (get_key(i - 1)->id == get_key(i)->id)
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
}
/* Erase any passwords from stack */
memset(line, 0, sizeof (line));
}
/* ================================================== */
@@ -405,7 +402,7 @@ check_auth(Key *key, const void *data, int data_len,
hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
return MIN(hash_len, trunc_len) == auth_len && UTI_IsMemoryEqual(buf, auth, auth_len);
}
/* ================================================== */

View File

@@ -124,13 +124,13 @@ get_list_leap(time_t when, int *tai_offset)
/* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
while (fgets(line, sizeof line, f) > 0) {
while (fgets(line, sizeof line, f)) {
int64_t lsl_when;
int lsl_tai_offset;
char *p;
/* Ignore blank lines */
for (p = line; *p && isspace(*p); ++p)
for (p = line; *p && isspace((unsigned char)*p); ++p)
;
if (!*p)
continue;

182
local.c
View File

@@ -97,8 +97,142 @@ static double precision_quantum;
static double max_clock_error;
#define NSEC_PER_SEC 1000000000
/* ================================================== */
/* Ask the system for the resolution of the system clock. The Linux
clock_getres() is not usable, because it reports the internal timer
resolution, which is 1 ns when high-resolution timers are enabled,
even when using a lower-resolution clocksource. */
static int
get_clock_resolution(void)
{
#if defined(HAVE_CLOCK_GETTIME) && !defined(LINUX)
struct timespec res;
if (clock_getres(CLOCK_REALTIME, &res) < 0)
return 0;
return NSEC_PER_SEC * res.tv_sec + res.tv_nsec;
#else
return 0;
#endif
}
/* ================================================== */
#if defined(LINUX) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC_RAW)
static int
compare_ints(const void *a, const void *b)
{
return *(const int *)a - *(const int *)b;
}
#define READINGS 64
/* On Linux, try to measure the actual resolution of the system
clock by performing a varying amount of busy work between clock
readings and finding the minimum change in the measured interval.
Require a change of at least two nanoseconds to ignore errors
caused by conversion to timespec. Use the raw monotonic clock
to avoid the impact of potential frequency changes due to NTP
adjustments made by other processes, and the kernel dithering of
the 32-bit multiplier. */
static int
measure_clock_resolution(void)
{
int i, j, b, busy, diffs[READINGS - 1], diff2, min;
struct timespec start_ts, ts[READINGS];
uint32_t acc;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_ts) < 0)
return 0;
for (acc = 0, busy = 1; busy < 100000; busy = busy * 3 / 2 + 1) {
for (i = 0, b = busy * READINGS; i < READINGS; i++, b -= busy) {
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts[i]) < 0)
return 0;
for (j = b; j > 0; j--)
acc += (acc & 1) + (uint32_t)ts[i].tv_nsec;
}
/* Give up after 0.1 seconds */
if (UTI_DiffTimespecsToDouble(&ts[READINGS - 1], &start_ts) > 0.1) {
DEBUG_LOG("Measurement too slow");
return 0;
}
for (i = 0; i < READINGS - 1; i++) {
diffs[i] = NSEC_PER_SEC * (ts[i + 1].tv_sec - ts[i].tv_sec) +
(ts[i + 1].tv_nsec - ts[i].tv_nsec);
/* Make sure the differences are sane. A resolution larger than the
reading time will be measured in measure_clock_read_delay(). */
if (diffs[i] <= 0 || diffs[i] > NSEC_PER_SEC)
return 0;
}
/* Sort the differences and keep values unique within 1 ns from the
first half of the array, which are less likely to be impacted by CPU
interruptions */
qsort(diffs, READINGS - 1, sizeof (diffs[0]), compare_ints);
for (i = 1, j = 0; i < READINGS / 2; i++) {
if (diffs[j] + 1 < diffs[i])
diffs[++j] = diffs[i];
}
j++;
#if 0
for (i = 0; i < j; i++)
DEBUG_LOG("busy %d diff %d %d", busy, i, diffs[i]);
#endif
/* Require at least three unique differences to be more confident
with the result */
if (j < 3)
continue;
/* Find the smallest difference between the unique differences */
for (i = 1, min = 0; i < j; i++) {
diff2 = diffs[i] - diffs[i - 1];
if (min == 0 || min > diff2)
min = diff2;
}
if (min == 0)
continue;
/* Prevent the compiler from optimising the busy work out */
if (acc == 0)
min += 1;
return min;
}
return 0;
}
#else
static int
measure_clock_resolution(void)
{
return 0;
}
#endif
/* ================================================== */
/* As a fallback, measure how long it takes to read the clock. It
typically takes longer than the resolution of the clock (and it
depends on the CPU speed), i.e. every reading gives a different
value, but handle also low-resolution clocks that might give
the same reading multiple times. */
/* Define the number of increments of the system clock that we want
to see to be fairly sure that we've got something approaching
the minimum increment. Even on a crummy implementation that can't
@@ -106,10 +240,8 @@ static double max_clock_error;
under 1s of busy waiting. */
#define NITERS 100
#define NSEC_PER_SEC 1000000000
static double
measure_clock_precision(void)
static int
measure_clock_read_delay(void)
{
struct timespec ts, old_ts;
int iters, diff, best;
@@ -135,7 +267,28 @@ measure_clock_precision(void)
assert(best > 0);
return 1.0e-9 * best;
return best;
}
/* ================================================== */
static double
measure_clock_precision(void)
{
int res, delay, prec;
res = get_clock_resolution();
if (res <= 0)
res = measure_clock_resolution();
delay = measure_clock_read_delay();
if (res > 0)
prec = MIN(res, delay);
else
prec = delay;
return prec / 1.0e9;
}
/* ================================================== */
@@ -184,10 +337,8 @@ void
LCL_Finalise(void)
{
/* Make sure all handlers have been removed */
if (change_list.next != &change_list)
assert(0);
if (dispersion_notify_list.next != &dispersion_notify_list)
assert(0);
BRIEF_ASSERT(change_list.next == &change_list);
BRIEF_ASSERT(dispersion_notify_list.next == &dispersion_notify_list);
}
/* ================================================== */
@@ -225,9 +376,7 @@ LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything
/* Check that the handler is not already registered */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
if (!(ptr->handler != handler || ptr->anything != anything)) {
assert(0);
}
BRIEF_ASSERT(ptr->handler != handler || ptr->anything != anything);
}
new_entry = MallocNew(ChangeListEntry);
@@ -301,9 +450,7 @@ LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anythi
/* Check that the handler is not already registered */
for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
if (!(ptr->handler != handler || ptr->anything != anything)) {
assert(0);
}
BRIEF_ASSERT(ptr->handler != handler || ptr->anything != anything);
}
new_entry = MallocNew(DispersionNotifyListEntry);
@@ -701,8 +848,11 @@ LCL_MakeStep(void)
/* Cancel remaining slew and make the step */
LCL_AccumulateOffset(correction, 0.0);
if (!LCL_ApplyStepOffset(-correction))
if (!LCL_ApplyStepOffset(-correction)) {
/* Revert the correction */
LCL_AccumulateOffset(-correction, 0.0);
return 0;
}
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);

View File

@@ -46,6 +46,7 @@ static LOG_Context log_contexts;
/* Flag indicating we have initialised */
static int initialised = 0;
static char *file_log_path = NULL;
static FILE *file_log = NULL;
static int system_log = 0;
@@ -90,8 +91,11 @@ LOG_Finalise(void)
if (system_log)
closelog();
if (file_log)
if (file_log && file_log != stderr)
fclose(file_log);
file_log = NULL;
Free(file_log_path);
file_log_path = NULL;
LOG_CycleLogFiles();
@@ -200,27 +204,42 @@ void LOG_Message(LOG_Severity severity,
/* ================================================== */
void
LOG_OpenFileLog(const char *log_file)
static FILE *
open_file_log(const char *log_file, char mode)
{
FILE *f;
if (log_file) {
f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640);
f = UTI_OpenFile(NULL, log_file, NULL, mode, 0640);
} else {
f = stderr;
}
/* Enable line buffering */
setvbuf(f, NULL, _IOLBF, BUFSIZ);
if (f)
setvbuf(f, NULL, _IOLBF, BUFSIZ);
return f;
}
/* ================================================== */
void
LOG_OpenFileLog(const char *log_file)
{
if (file_log_path)
Free(file_log_path);
if (log_file)
file_log_path = Strdup(log_file);
else
file_log_path = NULL;
if (file_log && file_log != stderr)
fclose(file_log);
file_log = f;
file_log = open_file_log(file_log_path, 'A');
}
/* ================================================== */
void
@@ -385,14 +404,29 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
void
LOG_CycleLogFiles(void)
{
struct stat st;
LOG_FileID i;
FILE *f;
/* The log will be opened later when an entry is logged */
for (i = 0; i < n_filelogs; i++) {
if (logfiles[i].file)
fclose(logfiles[i].file);
logfiles[i].file = NULL;
logfiles[i].writes = 0;
}
/* Try to open the log specified by the -l option, but only if nothing is
present at its path to avoid unnecessary error messages. Keep the
original file if that fails (the process might no longer have the
necessary privileges to write in the directory). */
if (file_log && file_log != stderr && stat(file_log_path, &st) < 0 && errno == ENOENT) {
f = open_file_log(file_log_path, 'a');
if (f) {
fclose(file_log);
file_log = f;
}
}
}
/* ================================================== */

39
main.c
View File

@@ -107,11 +107,40 @@ delete_pidfile(void)
/* ================================================== */
static void
notify_system_manager(int start)
{
#ifdef LINUX
/* The systemd protocol is documented in the sd_notify(3) man page */
const char *message, *path = getenv("NOTIFY_SOCKET");
int sock_fd;
if (!path)
return;
if (path[0] != '/')
LOG_FATAL("Unsupported notification socket");
message = start ? "READY=1" : "STOPPING=1";
sock_fd = SCK_OpenUnixDatagramSocket(path, NULL, 0);
if (sock_fd < 0 || SCK_Send(sock_fd, message, strlen(message), 0) != strlen(message))
LOG_FATAL("Could not send notification to $NOTIFY_SOCKET");
SCK_CloseSocket(sock_fd);
#endif
}
/* ================================================== */
void
MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
notify_system_manager(0);
LCL_CancelOffsetCorrection();
SRC_DumpSources();
@@ -215,6 +244,8 @@ post_init_ntp_hook(void *anything)
REF_SetMode(ref_mode);
}
notify_system_manager(1);
/* Send an empty message to the foreground process so it can exit.
If that fails, indicating the process was killed, exit too. */
if (!LOG_NotifyParent(""))
@@ -339,8 +370,11 @@ go_daemon(void)
/* Don't exit before the 'parent' */
waitpid(pid, NULL, 0);
close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message));
close(pipefd[0]);
close(pipefd[1]);
if (r != 1 || message[0] != '\0') {
if (r > 1) {
/* Print the error message from the child */
@@ -351,8 +385,6 @@ go_daemon(void)
} else
exit(0);
} else {
close(pipefd[0]);
setsid();
/* Do 2nd fork, as-per recommended practice for launching daemons. */
@@ -362,6 +394,7 @@ go_daemon(void)
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
/* In the 'parent' */
close(pipefd[0]);
close(pipefd[1]);
exit(0);
} else {

View File

@@ -286,6 +286,7 @@ static ARR_Instance broadcasts;
/* Parameters for the peer delay quantile */
#define DELAY_QUANT_Q 100
#define DELAY_QUANT_LARGE_STEP_DELAY 100
#define DELAY_QUANT_REPEAT 7
/* Minimum and maximum allowed poll interval */
@@ -685,11 +686,13 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SRC_NTP, NAU_IsAuthEnabled(result->auth),
params->sel_options, &result->remote_addr.ip_addr,
params->min_samples, params->max_samples,
params->min_delay, params->asymmetry);
params->min_delay, params->asymmetry,
params->max_unreach);
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,
DELAY_QUANT_LARGE_STEP_DELAY,
LCL_GetSysPrecisionAsQuantum() / 2.0);
} else {
result->delay_quant = NULL;
@@ -1742,7 +1745,7 @@ check_delay_quant(NCR_Instance inst, double delay)
quant = QNT_GetQuantile(inst->delay_quant, QNT_GetMinK(inst->delay_quant));
if (delay <= quant)
if (delay <= quant + QNT_GetMinStep(inst->delay_quant) / 2.0)
return 1;
DEBUG_LOG("maxdelayquant: delay=%e quant=%e", delay, quant);
@@ -1958,7 +1961,7 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
/* The skew and estimated frequency offset relative to the remote source */
double skew, source_freq_lo, source_freq_hi;
/* RFC 5905 packet tests */
/* RFC 5905 and RFC 9769 packet tests */
int test1, test2n, test2i, test2, test3, test5, test6, test7;
int interleaved_packet, valid_packet, synced_packet;
@@ -2022,7 +2025,7 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
}
/* Check if the packet is valid per RFC 5905, section 8.
/* Check if the packet is valid per RFC 5905 (section 8) and RFC 9769.
The test values are 1 when passed and 0 when failed. */
/* Test 1 checks for duplicate packet */
@@ -2335,9 +2338,8 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
inst->valid_rx = 1;
}
if ((unsigned int)local_receive.source >= sizeof (tss_chars) ||
(unsigned int)local_transmit.source >= sizeof (tss_chars))
assert(0);
BRIEF_ASSERT((unsigned int)local_receive.source < sizeof (tss_chars) &&
(unsigned int)local_transmit.source < sizeof (tss_chars));
DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%.9f root_disp=%.9f refid=%"PRIx32" [%s]",
message->lvm, message->stratum, message->poll, message->precision,

View File

@@ -220,7 +220,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
SCK_CloseSocket(sock_fd);
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
phc_fd = SYS_Linux_OpenPHC(req.ifr_name, O_RDONLY);
if (phc_fd < 0)
return 0;
@@ -519,7 +519,7 @@ 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;
int n_readings, quality;
if (!HCL_NeedsNewSample(iface->clock, now))
return;
@@ -543,7 +543,8 @@ poll_phc(struct Interface *iface, struct timespec *now)
return;
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err))
&sample_phc_ts, &sample_sys_ts, &phc_err, &quality) ||
quality <= 0)
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);

View File

@@ -457,9 +457,8 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
if (replacement)
record->resolved_addr = new_addr->ip_addr;
if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
assert(0);
BRIEF_ASSERT(record->remote_addr == NCR_GetRemoteAddress(record->data) &&
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) == 0);
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
if (auto_start_sources)

View File

@@ -520,8 +520,7 @@ generate_key(int index)
ServerKey *key;
int key_length;
if (index < 0 || index >= MAX_SERVER_KEYS)
assert(0);
BRIEF_ASSERT(index >= 0 && index < MAX_SERVER_KEYS);
/* Prefer AES-128-GCM-SIV if available. Note that if older keys loaded
from ntsdumpdir use a different algorithm, responding to NTP requests
@@ -534,11 +533,11 @@ generate_key(int index)
key = &server_keys[index];
key_length = SIV_GetKeyLength(algorithm);
if (key_length > sizeof (key->key))
assert(0);
BRIEF_ASSERT(key_length <= sizeof (key->key));
UTI_GetRandomBytesUrandom(key->key, key_length);
memset(key->key + key_length, 0, sizeof (key->key) - key_length);
if (key_length < sizeof (key->key))
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 */
@@ -702,7 +701,7 @@ key_timeout(void *arg)
/* ================================================== */
static void
run_helper(uid_t uid, gid_t gid, int scfilter_level)
run_helper(uid_t uid, gid_t gid, int scfilter_level, int sock_fd)
{
LOG_Severity log_severity;
@@ -729,10 +728,15 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
if (scfilter_level != 0)
SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER);
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, handle_helper_request, NULL);
SCH_MainLoop();
DEBUG_LOG("Helper exiting");
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
NKS_Finalise();
SCK_Finalise();
SYS_Finalise();
@@ -792,9 +796,8 @@ NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
LOG_CloseParentFd();
SCK_CloseSocket(sock_fd1);
SCH_AddFileHandler(sock_fd2, SCH_FILE_INPUT, handle_helper_request, NULL);
run_helper(uid, gid, scfilter_level);
run_helper(uid, gid, scfilter_level, sock_fd2);
}
SCK_CloseSocket(sock_fd2);
@@ -957,8 +960,7 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
header->key_id = htonl(key->id);
nonce = cookie->cookie + sizeof (*header);
if (key->nonce_length > sizeof (cookie->cookie) - sizeof (*header))
assert(0);
BRIEF_ASSERT(key->nonce_length <= sizeof (cookie->cookie) - sizeof (*header));
UTI_GetRandomBytes(nonce, key->nonce_length);
plaintext_length = context->c2s.length + context->s2c.length;

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020-2021
* Copyright (C) Anthony Brandon 2025
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -37,11 +38,9 @@
#include "siv.h"
#include "socket.h"
#include "sched.h"
#include "tls.h"
#include "util.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#define INVALID_SOCK_FD (-8)
struct RecordHeader {
@@ -75,7 +74,7 @@ struct NKSN_Instance_Record {
KeState state;
int sock_fd;
char *label;
gnutls_session_t tls_session;
TLS_Instance tls_session;
SCH_TimeoutID timeout_id;
int retry_factor;
@@ -85,8 +84,6 @@ struct NKSN_Instance_Record {
/* ================================================== */
static gnutls_priority_t priority_cache;
static int credentials_counter = 0;
static int clock_updates = 0;
@@ -206,71 +203,6 @@ check_message_format(struct Message *message, int eof)
/* ================================================== */
static gnutls_session_t
create_tls_session(int server_mode, int sock_fd, const char *server_name,
gnutls_certificate_credentials_t credentials,
gnutls_priority_t priority)
{
unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
gnutls_session_t session;
gnutls_datum_t alpn;
unsigned int flags;
int r;
r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
if (r < 0) {
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
return NULL;
}
if (!server_mode) {
assert(server_name);
if (!UTI_IsStringIP(server_name)) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
}
flags = 0;
if (clock_updates < CNF_GetNoCertTimeCheck()) {
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
DEBUG_LOG("Disabled time checks");
}
gnutls_session_set_verify_cert(session, server_name, flags);
}
r = gnutls_priority_set(session, priority);
if (r < 0)
goto error;
r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, credentials);
if (r < 0)
goto error;
memcpy(alpn_name, NKE_ALPN_NAME, sizeof (alpn_name));
alpn.data = alpn_name;
alpn.size = sizeof (alpn_name) - 1;
r = gnutls_alpn_set_protocols(session, &alpn, 1, 0);
if (r < 0)
goto error;
gnutls_transport_set_int(session, sock_fd);
return session;
error:
LOG(LOGS_ERR, "Could not %s TLS session : %s", "set", gnutls_strerror(r));
gnutls_deinit(session);
return NULL;
}
/* ================================================== */
static void
stop_session(NKSN_Instance inst)
{
@@ -286,7 +218,7 @@ stop_session(NKSN_Instance inst)
Free(inst->label);
inst->label = NULL;
gnutls_deinit(inst->tls_session);
TLS_DestroyInstance(inst->tls_session);
inst->tls_session = NULL;
SCH_RemoveTimeout(inst->timeout_id);
@@ -308,21 +240,6 @@ session_timeout(void *arg)
/* ================================================== */
static int
check_alpn(NKSN_Instance inst)
{
gnutls_datum_t alpn;
if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
return 0;
return 1;
}
/* ================================================== */
static void
set_input_output(NKSN_Instance inst, int output)
{
@@ -364,6 +281,7 @@ static int
handle_event(NKSN_Instance inst, int event)
{
struct Message *message = &inst->message;
TLS_Status s;
int r;
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
@@ -390,56 +308,28 @@ handle_event(NKSN_Instance inst, int event)
return 0;
case KE_HANDSHAKE:
r = gnutls_handshake(inst->tls_session);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
gnutls_datum_t cert_error;
/* Get a description of verification errors */
if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
gnutls_certificate_verification_status_print(
gnutls_session_get_verify_cert_status(inst->tls_session),
gnutls_certificate_type_get(inst->tls_session), &cert_error, 0) < 0)
cert_error.data = NULL;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
if (cert_error.data)
gnutls_free(cert_error.data);
s = TLS_DoHandshake(inst->tls_session);
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_OUTPUT:
case TLS_AGAIN_INPUT:
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
return 0;
default:
stop_session(inst);
/* Increase the retry interval if the handshake did not fail due
to the other end closing the connection */
if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
if (s != TLS_CLOSED)
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
return 0;
}
/* Disable output when the handshake is trying to receive data */
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
return 0;
}
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
if (DEBUG) {
char *description = gnutls_session_get_desc(inst->tls_session);
DEBUG_LOG("Handshake with %s completed %s",
inst->label, description ? description : "");
gnutls_free(description);
}
if (!check_alpn(inst)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
stop_session(inst);
return 0;
}
/* Client will send a request to the server */
change_state(inst, inst->server ? KE_RECEIVE : KE_SEND);
return 0;
@@ -448,16 +338,17 @@ handle_event(NKSN_Instance inst, int event)
assert(inst->new_message && message->complete);
assert(message->length <= sizeof (message->data) && message->length > message->sent);
r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
message->length - message->sent);
s = TLS_Send(inst->tls_session, &message->data[message->sent],
message->length - message->sent, &r);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not send NTS-KE message to %s : %s", inst->label, gnutls_strerror(r));
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_OUTPUT:
return 0;
default:
stop_session(inst);
}
return 0;
return 0;
}
DEBUG_LOG("Sent %d bytes to %s", r, inst->label);
@@ -480,26 +371,24 @@ handle_event(NKSN_Instance inst, int event)
return 0;
}
r = gnutls_record_recv(inst->tls_session, &message->data[message->length],
sizeof (message->data) - message->length);
s = TLS_Receive(inst->tls_session, &message->data[message->length],
sizeof (message->data) - message->length, &r);
if (r < 0) {
/* Handle a renegotiation request on both client and server as
a protocol error */
if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Could not receive NTS-KE message from %s : %s",
inst->label, gnutls_strerror(r));
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_INPUT:
return 0;
default:
stop_session(inst);
}
return 0;
return 0;
}
DEBUG_LOG("Received %d bytes from %s", r, inst->label);
message->length += r;
} while (gnutls_record_check_pending(inst->tls_session) > 0);
} while (TLS_CheckPending(inst->tls_session));
if (!check_message_format(message, r == 0)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
@@ -519,18 +408,18 @@ handle_event(NKSN_Instance inst, int event)
return 1;
case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
s = TLS_Shutdown(inst->tls_session);
if (r < 0) {
if (gnutls_error_is_fatal(r)) {
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
switch (s) {
case TLS_SUCCESS:
break;
case TLS_AGAIN_OUTPUT:
case TLS_AGAIN_INPUT:
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
return 0;
default:
stop_session(inst);
return 0;
}
/* Disable output when the TLS shutdown is trying to receive data */
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
return 0;
}
SCK_ShutdownConnection(inst->sock_fd);
@@ -592,36 +481,18 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
static int gnutls_initialised = 0;
static int tls_initialised = 0;
static int
init_gnutls(void)
init_tls(void)
{
int r;
if (gnutls_initialised)
if (tls_initialised)
return 1;
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
/* Prepare a priority cache for server and client NTS-KE sessions
(the NTS specification requires TLS1.3 or later) */
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(LOGS_ERR, "Could not initialise %s : %s",
"priority cache for TLS", gnutls_strerror(r));
gnutls_global_deinit();
if (!TLS_Initialise(&get_time))
return 0;
}
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time);
gnutls_initialised = 1;
tls_initialised = 1;
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL);
@@ -632,16 +503,15 @@ init_gnutls(void)
/* ================================================== */
static void
deinit_gnutls(void)
deinit_tls(void)
{
if (!gnutls_initialised || credentials_counter > 0)
if (!tls_initialised || credentials_counter > 0)
return;
LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
gnutls_initialised = 0;
TLS_Finalise();
tls_initialised = 0;
DEBUG_LOG("Deinitialised");
}
@@ -652,69 +522,21 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
const char **trusted_certs, uint32_t *trusted_certs_ids,
int n_trusted_certs, uint32_t trusted_cert_set)
{
gnutls_certificate_credentials_t credentials = NULL;
int i, r;
TLS_Credentials credentials;
if (!init_gnutls())
if (!init_tls())
return NULL;
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
goto error;
if (certs && keys) {
if (trusted_certs || trusted_certs_ids)
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)
goto error;
}
} else {
if (certs || keys || n_certs_keys > 0)
assert(0);
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0)
goto error;
}
if (trusted_certs && trusted_certs_ids) {
for (i = 0; i < n_trusted_certs; i++) {
struct stat buf;
if (trusted_certs_ids[i] != trusted_cert_set)
continue;
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
else
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
}
}
credentials = TLS_CreateCredentials(certs, keys, n_certs_keys, trusted_certs,
trusted_certs_ids, n_trusted_certs, trusted_cert_set);
if (!credentials) {
deinit_tls();
return NULL;
}
credentials_counter++;
return (NKSN_Credentials)credentials;
error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
if (credentials)
gnutls_certificate_free_credentials(credentials);
deinit_gnutls();
return NULL;
return credentials;
}
/* ================================================== */
@@ -739,9 +561,9 @@ NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
void
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
{
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
TLS_DestroyCredentials(credentials);
credentials_counter--;
deinit_gnutls();
deinit_tls();
}
/* ================================================== */
@@ -791,9 +613,9 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
{
assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
(gnutls_certificate_credentials_t)credentials,
priority_cache);
inst->tls_session = TLS_CreateInstance(inst->server, sock_fd, inst->server_name,
label, NKE_ALPN_NAME, credentials,
clock_updates < CNF_GetNoCertTimeCheck());
if (!inst->tls_session)
return 0;
@@ -901,19 +723,15 @@ NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter
context.algorithm = htons(exporter_algorithm);
context.is_s2c = 0;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, (char *)&context,
length, (char *)c2s->key) < 0) {
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, &context, length, c2s->key)) {
DEBUG_LOG("Could not export key");
return 0;
}
context.is_s2c = 1;
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, (char *)&context,
length, (char *)s2c->key) < 0) {
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (context) - 1, &context, length, s2c->key)) {
DEBUG_LOG("Could not export key");
return 0;
}

View File

@@ -104,9 +104,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
body = (unsigned char *)(header + 1);
ciphertext = body + nonce_length + nonce_padding;
if ((unsigned char *)header + auth_length !=
ciphertext + ciphertext_length + ciphertext_padding + additional_padding)
assert(0);
BRIEF_ASSERT((unsigned char *)header + auth_length ==
ciphertext + ciphertext_length + ciphertext_padding + additional_padding);
memcpy(body, nonce, nonce_length);
memset(body + nonce_length, 0, nonce_padding);

View File

@@ -259,8 +259,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
/* Make sure this is a response to the request from the last call
of NNS_CheckRequestAuth() */
if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
assert(0);
BRIEF_ASSERT(UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) == 0);
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
if (!NEF_ParseField(request, req_info->length, parsed,

View File

@@ -55,7 +55,7 @@ struct request_length {
};
static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(null, null), /* NULL */
{ 0, 0 }, /* NULL - not supported */
REQ_LENGTH_ENTRY(online, null), /* ONLINE */
REQ_LENGTH_ENTRY(offline, null), /* OFFLINE */
REQ_LENGTH_ENTRY(burst, null), /* BURST */
@@ -65,7 +65,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(modify_maxdelay, null), /* MODIFY_MAXDELAY */
REQ_LENGTH_ENTRY(modify_maxdelayratio, null), /* MODIFY_MAXDELAYRATIO */
REQ_LENGTH_ENTRY(modify_maxupdateskew, null), /* MODIFY_MAXUPDATESKEW */
REQ_LENGTH_ENTRY(logon, null), /* LOGON */
{ 0, 0 }, /* LOGON - not supported */
REQ_LENGTH_ENTRY(settime, manual_timestamp), /* SETTIME */
{ 0, 0 }, /* LOCAL */
REQ_LENGTH_ENTRY(manual, null), /* MANUAL */

View File

@@ -141,6 +141,7 @@ have_helper(void)
/* ======================================================================= */
/* HELPER - prepare fatal error for daemon */
FORMAT_ATTRIBUTE_PRINTF(2, 3)
static void
res_fatal(PrvResponse *res, const char *fmt, ...)
{
@@ -546,9 +547,9 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
PrvResponse res;
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort())
assert(0);
BRIEF_ASSERT(ip_saddr.port == 0 || ip_saddr.port == CNF_GetNTPPort() ||
ip_saddr.port == CNF_GetAcquisitionPort() ||
ip_saddr.port == CNF_GetPtpPort());
if (!have_helper())
return bind(sock, address, address_len);

View File

@@ -49,20 +49,21 @@ struct QNT_Instance_Record {
int q;
int min_k;
double min_step;
double neg_step_limit;
int n_set;
};
/* ================================================== */
QNT_Instance
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
QNT_CreateInstance(int min_k, int max_k, int q, int repeat,
int large_step_delay, 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);
BRIEF_ASSERT(q >= 2 && min_k <= max_k && min_k >= 1 && max_k < q && repeat >= 1 &&
repeat <= MAX_REPEAT && min_step > 0.0 && large_step_delay >= 0);
inst = MallocNew(struct QNT_Instance_Record);
inst->n_quants = (max_k - min_k + 1) * repeat;
@@ -71,6 +72,7 @@ QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
inst->q = q;
inst->min_k = min_k;
inst->min_step = min_step;
inst->neg_step_limit = -large_step_delay * min_step;
QNT_Reset(inst);
@@ -114,8 +116,7 @@ insert_initial_value(QNT_Instance inst, double value)
{
int i, j, r = inst->repeat;
if (inst->n_set * r >= inst->n_quants)
assert(0);
BRIEF_ASSERT(inst->n_set * r < inst->n_quants);
/* Keep the initial estimates repeated and ordered */
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
@@ -136,29 +137,36 @@ insert_initial_value(QNT_Instance inst, double value)
static void
update_estimate(struct Quantile *quantile, double value, double p, double rand,
double min_step)
double min_step, double neg_step_limit)
{
if (value > quantile->est && rand > (1.0 - p)) {
if (value >= quantile->est) {
if (rand < (1.0 - p))
return;
quantile->step += quantile->sign > 0 ? min_step : -min_step;
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
quantile->est += quantile->step > min_step ? quantile->step : min_step;
if (quantile->est > value) {
quantile->step += value - quantile->est;
quantile->est = value;
quantile->est = value + min_step / 4.0;
}
if (quantile->sign < 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = 1;
} else if (value < quantile->est && rand > p) {
} else {
if (rand < p)
return;
quantile->step += quantile->sign < 0 ? min_step : -min_step;
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
quantile->est -= quantile->step > min_step ? quantile->step : min_step;
if (quantile->est < value) {
quantile->step += quantile->est - value;
quantile->est = value;
quantile->est = value - min_step / 4.0;
}
if (quantile->sign > 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = -1;
}
if (quantile->step < neg_step_limit)
quantile->step = neg_step_limit;
}
/* ================================================== */
@@ -179,7 +187,7 @@ QNT_Accumulate(QNT_Instance inst, double value)
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);
update_estimate(&inst->quants[i], value, p, rand, inst->min_step, inst->neg_step_limit);
}
}
@@ -193,14 +201,29 @@ QNT_GetMinK(QNT_Instance inst)
/* ================================================== */
int
QNT_GetMaxK(QNT_Instance inst)
{
return inst->min_k + (inst->n_quants / inst->repeat) - 1;
}
/* ================================================== */
double
QNT_GetMinStep(QNT_Instance inst)
{
return inst->min_step;
}
/* ================================================== */
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);
BRIEF_ASSERT(k >= inst->min_k && (k - inst->min_k) * inst->repeat < inst->n_quants);
for (i = 0; i < inst->repeat; i++)
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;

View File

@@ -30,12 +30,15 @@
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 QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat,
int large_step_delay, 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 int QNT_GetMaxK(QNT_Instance inst);
extern double QNT_GetMinStep(QNT_Instance inst);
extern double QNT_GetQuantile(QNT_Instance inst, int k);
#endif

View File

@@ -48,6 +48,7 @@ extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver;
extern RefclockDriver RCL_PPS_driver;
extern RefclockDriver RCL_PHC_driver;
extern RefclockDriver RCL_RTC_driver;
struct FilterSample {
double offset;
@@ -63,6 +64,7 @@ struct RCL_Instance_Record {
int driver_poll;
int driver_polled;
int poll;
int reached;
int leap_status;
int local;
int pps_forced;
@@ -159,6 +161,8 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver = &RCL_PPS_driver;
} else if (strcmp(params->driver_name, "PHC") == 0) {
inst->driver = &RCL_PHC_driver;
} else if (strcmp(params->driver_name, "RTC") == 0) {
inst->driver = &RCL_RTC_driver;
} else {
LOG_FATAL("unknown refclock driver %s", params->driver_name);
}
@@ -175,6 +179,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_poll = params->driver_poll;
inst->poll = params->poll;
inst->driver_polled = 0;
inst->reached = 0;
inst->leap_status = LEAP_Normal;
inst->local = params->local;
inst->pps_forced = params->pps_forced;
@@ -226,33 +231,20 @@ RCL_AddRefclock(RefclockParameters *params)
}
if (inst->driver->poll) {
int max_samples;
if (inst->driver_poll > inst->poll)
inst->driver_poll = inst->poll;
max_samples = 1 << (inst->poll - inst->driver_poll);
if (max_samples < params->filter_length) {
if (max_samples < 4) {
LOG(LOGS_WARN, "Setting filter length for %s to %d",
UTI_RefidToString(inst->ref_id), max_samples);
}
params->filter_length = max_samples;
}
}
if (inst->driver->init && !inst->driver->init(inst))
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
/* Require the filter to have at least 4 samples to produce a filtered
sample, or be full for shorter lengths, and combine 60% of samples
closest to the median */
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
params->max_dispersion, 0.6);
/* Don't require more than one sample per poll and combine 60% of the
samples closest to the median offset */
inst->filter = SPF_CreateInstance(1, params->filter_length, params->max_dispersion, 0.6);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
NULL, params->min_samples, params->max_samples,
0.0, 0.0);
0.0, 0.0, params->max_unreach);
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id),
@@ -435,10 +427,21 @@ convert_tai_offset(struct timespec *sample_time, double *offset)
}
static int
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion,
int quality)
{
NTP_Sample sample;
instance->reached++;
/* Don't accumulate the sample if the driver is suggesting it should be
dropped due to low quality. The only reason it went so far was to update
the reachability. */
if (quality < 1) {
DEBUG_LOG("refclock sample ignored quality=%d", quality);
return 0;
}
sample.time = *sample_time;
sample.offset = offset;
sample.peer_delay = instance->delay;
@@ -451,14 +454,14 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap)
struct timespec *ref_time, int leap, int quality)
{
double correction, dispersion, raw_offset, offset;
struct timespec cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time,
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec), quality);
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
@@ -494,7 +497,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
return 0;
}
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
if (!accumulate_sample(instance, &cooked_time, offset, dispersion, quality))
return 0;
instance->pps_active = 0;
@@ -509,7 +512,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
}
int
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second, int quality)
{
double correction, dispersion;
struct timespec cooked_time;
@@ -521,7 +524,7 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
return 0;
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction, quality);
}
static int
@@ -547,7 +550,7 @@ check_pulse_edge(RCL_Instance instance, double offset, double distance)
int
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction)
double second, double dispersion, double raw_correction, int quality)
{
double offset;
int rate;
@@ -649,7 +652,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 0;
}
if (!accumulate_sample(instance, cooked_time, offset, dispersion))
if (!accumulate_sample(instance, cooked_time, offset, dispersion, quality))
return 0;
instance->leap_status = leap;
@@ -792,6 +795,9 @@ poll_timeout(void *arg)
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
inst->driver_polled = 0;
SRC_UpdateReachability(inst->source, inst->reached > 0);
inst->reached = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) {
double local_freq, local_offset;
struct timespec local_ref_time;
@@ -807,7 +813,6 @@ poll_timeout(void *arg)
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);
@@ -816,8 +821,6 @@ poll_timeout(void *arg)
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

@@ -42,6 +42,7 @@ typedef struct {
int pps_rate;
int min_samples;
int max_samples;
int max_unreach;
int sel_options;
int max_lock_age;
int stratum;
@@ -77,10 +78,12 @@ 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,
struct timespec *ref_time, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
struct timespec *ref_time, int leap, int quality);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second,
int quality);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);
double second, double dispersion, double raw_correction,
int quality);
extern double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance);

View File

@@ -66,7 +66,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;
int rising_edge;
struct stat st;
char *path, *s;
@@ -74,19 +74,20 @@ static int phc_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance);
phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0)
LOG_FATAL("Could not open PHC");
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->fd = SYS_Linux_OpenPHC(path, phc->extpps ? O_RDWR : O_RDONLY);
if (phc->fd < 0)
LOG_FATAL("Could not open PHC");
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->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance));
@@ -158,7 +159,7 @@ static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
UTI_DiffTimespecsToDouble(phc_ts, &local_ts), 1);
}
static void read_ext_pulse(int fd, int event, void *anything)
@@ -195,7 +196,7 @@ static int phc_poll(RCL_Instance instance)
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
struct phc_instance *phc;
double phc_err, local_err;
int n_readings;
int n_readings, quality;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
@@ -204,11 +205,13 @@ static int phc_poll(RCL_Instance instance)
if (n_readings < 1)
return 0;
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
if (!HCL_ProcessReadings(phc->clock, n_readings, readings,
&phc_ts, &sys_ts, &phc_err, &quality))
return 0;
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (quality > 0)
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (phc->extpps)
return 0;
@@ -216,7 +219,7 @@ static int phc_poll(RCL_Instance instance)
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal, quality);
}
RefclockDriver RCL_PHC_driver = {

View File

@@ -143,7 +143,7 @@ static int pps_poll(RCL_Instance instance)
pps->last_seq = seq;
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec, 1);
}
RefclockDriver RCL_PPS_driver = {

180
refclock_rtc.c Normal file
View File

@@ -0,0 +1,180 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Uwe Kleine-König, Pengutronix 2021
* Copyright (C) Ahmad Fatoum, Pengutronix 2024
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
RTC refclock driver.
*/
#include "config.h"
#include "refclock.h"
#ifdef FEAT_RTC
#include <linux/rtc.h>
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "sched.h"
#include "util.h"
#include "rtc_linux.h"
struct refrtc_instance {
int fd;
int polling;
int utc;
};
static int refrtc_add_sample(RCL_Instance instance, struct timespec *now,
time_t rtc_sec, long rtc_nsec)
{
struct timespec rtc_ts;
int status;
rtc_ts.tv_sec = rtc_sec;
rtc_ts.tv_nsec = rtc_nsec;
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal, 1);
return status;
}
static void refrtc_read_after_uie(int rtcfd, int event, void *data)
{
RCL_Instance instance = (RCL_Instance)data;
struct refrtc_instance *rtc = RCL_GetDriverData(instance);
struct timespec now;
time_t rtc_sec;
int status;
status = RTC_Linux_CheckInterrupt(rtcfd);
if (status < 0) {
SCH_RemoveFileHandler(rtcfd);
RTC_Linux_SwitchInterrupt(rtcfd, 0); /* Likely to raise error too, but just to be sure... */
close(rtcfd);
rtc->fd = -1;
return;
} else if (status == 0) {
/* Wait for the next interrupt, this one may be bogus */
return;
}
rtc_sec = RTC_Linux_ReadTimeAfterInterrupt(rtcfd, rtc->utc, NULL, &now);
if (rtc_sec == (time_t)-1)
return;
refrtc_add_sample(instance, &now, rtc_sec, 0);
}
static int refrtc_initialise(RCL_Instance instance)
{
const char *options[] = {"utc", NULL};
struct refrtc_instance *rtc;
int rtcfd, status;
const char *path;
RCL_CheckDriverOptions(instance, options);
if (CNF_GetRtcSync() || CNF_GetRtcFile())
LOG_FATAL("RTC refclock cannot be used together with rtcsync or rtcfile");
path = RCL_GetDriverParameter(instance);
rtcfd = open(path, O_RDONLY);
if (rtcfd < 0)
LOG_FATAL("Could not open RTC device %s : %s", path, strerror(errno));
/* Close on exec */
UTI_FdSetCloexec(rtcfd);
rtc = MallocNew(struct refrtc_instance);
rtc->fd = rtcfd;
rtc->utc = RCL_GetDriverOption(instance, "utc") ? 1 : 0;
RCL_SetDriverData(instance, rtc);
/* Try to enable update interrupts */
status = RTC_Linux_SwitchInterrupt(rtcfd, 1);
if (status) {
SCH_AddFileHandler(rtcfd, SCH_FILE_INPUT, refrtc_read_after_uie, instance);
rtc->polling = 0;
} else {
LOG(LOGS_INFO, "Falling back to polling for %s", path);
rtc->polling = 1;
}
return 1;
}
static void refrtc_finalise(RCL_Instance instance)
{
struct refrtc_instance *rtc;
rtc = RCL_GetDriverData(instance);
if (rtc->fd >= 0) {
if (!rtc->polling) {
SCH_RemoveFileHandler(rtc->fd);
RTC_Linux_SwitchInterrupt(rtc->fd, 0);
}
close(rtc->fd);
}
Free(rtc);
}
static int refrtc_poll(RCL_Instance instance)
{
struct refrtc_instance *rtc;
struct timespec now;
time_t rtc_sec;
rtc = RCL_GetDriverData(instance);
if (!rtc->polling)
return 0;
rtc_sec = RTC_Linux_ReadTimeNow(rtc->fd, rtc->utc, NULL, &now);
if (rtc_sec == (time_t)-1)
return 0;
/* As the rtc has a resolution of 1s, only add half a second */
return refrtc_add_sample(instance, &now, rtc_sec, 500000000);
}
RefclockDriver RCL_RTC_driver = {
refrtc_initialise,
refrtc_finalise,
refrtc_poll
};
#else
RefclockDriver RCL_RTC_driver = { NULL, NULL, NULL };
#endif

View File

@@ -124,7 +124,7 @@ static int shm_poll(RCL_Instance instance)
UTI_NormaliseTimespec(&clock_ts);
UTI_NormaliseTimespec(&receive_ts);
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap, 1);
}
RefclockDriver RCL_SHM_driver = {

View File

@@ -135,9 +135,9 @@ static void read_sample(int sockfd, int event, void *anything)
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
if (sample.pulse) {
RCL_AddPulse(instance, &sys_ts, sample.offset);
RCL_AddPulse(instance, &sys_ts, sample.offset, 1);
} else {
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap, 1);
}
}

View File

@@ -47,9 +47,6 @@
/* The update interval of the reference in the local reference mode */
#define LOCAL_REF_UPDATE_INTERVAL 64.0
/* Interval between updates of the drift file */
#define MAX_DRIFTFILE_AGE 3600.0
static int are_we_synchronised;
static int enable_local_stratum;
static int local_stratum;
@@ -57,6 +54,8 @@ static int local_orphan;
static double local_distance;
static int local_activate_ok;
static double local_activate;
static double local_wait_synced;
static double local_wait_unsynced;
static struct timespec local_ref_time;
static NTP_Leap our_leap_status;
static int our_leap_sec;
@@ -65,6 +64,7 @@ static int our_stratum;
static uint32_t our_ref_id;
static IPAddr our_ref_ip;
static struct timespec our_ref_time;
static double unsynchronised_since;
static double our_skew;
static double our_residual_freq;
static double our_root_delay;
@@ -110,6 +110,7 @@ static REF_ModeEndHandler mode_end_handler = NULL;
/* Filename of the drift file. */
static char *drift_file=NULL;
static double drift_file_age;
static int drift_file_interval;
static void update_drift_file(double, double);
@@ -213,7 +214,7 @@ REF_Initialise(void)
local_activate_ok = 0;
/* Now see if we can get the drift file opened */
drift_file = CNF_GetDriftFile();
drift_file = CNF_GetDriftFile(&drift_file_interval);
if (drift_file) {
in = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0);
if (in) {
@@ -250,8 +251,11 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
&local_distance, &local_activate);
&local_distance, &local_activate,
&local_wait_synced,
&local_wait_unsynced);
UTI_ZeroTimespec(&local_ref_time);
unsynchronised_since = SCH_GetLastEventMonoTime();
leap_when = 0;
leap_timeout_id = 0;
@@ -987,6 +991,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
if (step_offset != 0.0) {
if (LCL_ApplyStepOffset(step_offset))
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
else
LCL_AccumulateOffset(step_offset, 0.0);
}
update_leap_status(leap, raw_now.tv_sec, 0);
@@ -1005,7 +1011,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
if (drift_file) {
/* Update drift file at most once per hour */
drift_file_age += update_interval;
if (drift_file_age >= MAX_DRIFTFILE_AGE) {
if (drift_file_age >= drift_file_interval) {
update_drift_file(local_abs_frequency, our_skew);
drift_file_age = 0.0;
}
@@ -1095,6 +1101,9 @@ REF_SetUnsynchronised(void)
our_ref_ip.family = IPADDR_INET4;
our_ref_ip.addr.in4 = 0;
our_stratum = 0;
if (are_we_synchronised)
unsynchronised_since = SCH_GetLastEventMonoTime();
are_we_synchronised = 0;
LCL_SetSyncStatus(0, 0.0, 0.0);
@@ -1138,13 +1147,16 @@ REF_GetReferenceParams
)
{
double dispersion, delta, distance;
int wait_local_ok;
assert(initialised);
if (are_we_synchronised) {
dispersion = get_root_dispersion(local_time);
wait_local_ok = UTI_DiffTimespecsToDouble(local_time, &our_ref_time) >= local_wait_synced;
} else {
dispersion = 0.0;
wait_local_ok = SCH_GetLastEventMonoTime() - unsynchronised_since >= local_wait_unsynced;
}
distance = our_root_delay / 2 + dispersion;
@@ -1156,7 +1168,8 @@ REF_GetReferenceParams
or the root distance exceeds the threshold */
if (are_we_synchronised &&
!(enable_local_stratum && local_activate_ok && distance > local_distance)) {
!(enable_local_stratum && local_activate_ok && wait_local_ok &&
distance > local_distance)) {
*is_synchronised = 1;
@@ -1168,7 +1181,7 @@ REF_GetReferenceParams
*root_delay = our_root_delay;
*root_dispersion = dispersion;
} else if (enable_local_stratum && local_activate_ok) {
} else if (enable_local_stratum && local_activate_ok && wait_local_ok) {
*is_synchronised = 0;
@@ -1268,13 +1281,16 @@ REF_ModifyMakestep(int limit, double threshold)
/* ================================================== */
void
REF_EnableLocal(int stratum, double distance, int orphan, double activate)
REF_EnableLocal(int stratum, double distance, int orphan, double activate,
double wait_synced, double wait_unsynced)
{
enable_local_stratum = 1;
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
local_activate = activate;
local_wait_synced = wait_synced;
local_wait_unsynced = wait_unsynced;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}

View File

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

3
rtc.c
View File

@@ -81,8 +81,9 @@ get_driftfile_time(void)
{
struct stat buf;
char *drift_file;
int interval;
drift_file = CNF_GetDriftFile();
drift_file = CNF_GetDriftFile(&interval);
if (!drift_file)
return 0;

View File

@@ -4,6 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012-2014
* Copyright (C) Ahmad Fatoum, Pengutronix 2024
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -296,14 +297,25 @@ slew_samples
corresponding real time clock 'DMY HMS' form, taking account of
whether the user runs his RTC on the local time zone or UTC */
static struct tm *
rtc_from_t(const time_t *t)
static void
rtc_from_t(const time_t *t, struct rtc_time *rtc_raw, int utc)
{
if (rtc_on_utc) {
return gmtime(t);
struct tm *rtc_tm;
if (utc) {
rtc_tm = gmtime(t);
} else {
return localtime(t);
rtc_tm = localtime(t);
}
rtc_raw->tm_sec = rtc_tm->tm_sec;
rtc_raw->tm_min = rtc_tm->tm_min;
rtc_raw->tm_hour = rtc_tm->tm_hour;
rtc_raw->tm_mday = rtc_tm->tm_mday;
rtc_raw->tm_mon = rtc_tm->tm_mon;
rtc_raw->tm_year = rtc_tm->tm_year;
rtc_raw->tm_wday = rtc_tm->tm_wday;
rtc_raw->tm_yday = rtc_tm->tm_yday;
rtc_raw->tm_isdst = rtc_tm->tm_isdst;
}
/* ================================================== */
@@ -341,17 +353,27 @@ rtc_from_t(const time_t *t)
*/
static time_t
t_from_rtc(struct tm *stm) {
struct tm temp1, temp2, *tm;
t_from_rtc(struct rtc_time *rtc_raw, int utc)
{
struct tm rtc_tm, temp1, temp2, *tm;
long diff;
time_t t1, t2;
temp1 = *stm;
/* Convert to seconds since 1970 */
memset(&rtc_tm, 0, sizeof (rtc_tm));
rtc_tm.tm_sec = rtc_raw->tm_sec;
rtc_tm.tm_min = rtc_raw->tm_min;
rtc_tm.tm_hour = rtc_raw->tm_hour;
rtc_tm.tm_mday = rtc_raw->tm_mday;
rtc_tm.tm_mon = rtc_raw->tm_mon;
rtc_tm.tm_year = rtc_raw->tm_year;
temp1 = rtc_tm;
temp1.tm_isdst = 0;
t1 = mktime(&temp1);
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
tm = utc ? gmtime(&t1) : localtime(&t1);
if (!tm) {
DEBUG_LOG("gmtime()/localtime() failed");
return -1;
@@ -476,8 +498,8 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
/* ================================================== */
static int
switch_interrupts(int on_off)
int
RTC_Linux_SwitchInterrupt(int fd, int on_off)
{
if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
@@ -508,7 +530,7 @@ RTC_Linux_Initialise(void)
}
/* Make sure the RTC supports interrupts */
if (!switch_interrupts(1) || !switch_interrupts(0)) {
if (!RTC_Linux_SwitchInterrupt(fd, 1) || !RTC_Linux_SwitchInterrupt(fd, 0)) {
close(fd);
return 0;
}
@@ -557,7 +579,7 @@ RTC_Linux_Finalise(void)
/* Remove input file handler */
if (fd >= 0) {
SCH_RemoveFileHandler(fd);
switch_interrupts(0);
RTC_Linux_SwitchInterrupt(fd, 0);
close(fd);
/* Save the RTC data */
@@ -578,7 +600,7 @@ static void
measurement_timeout(void *any)
{
timeout_id = 0;
switch_interrupts(1);
RTC_Linux_SwitchInterrupt(fd, 1);
}
/* ================================================== */
@@ -586,21 +608,10 @@ measurement_timeout(void *any)
static void
set_rtc(time_t new_rtc_time)
{
struct tm rtc_tm;
struct rtc_time rtc_raw;
int status;
rtc_tm = *rtc_from_t(&new_rtc_time);
rtc_raw.tm_sec = rtc_tm.tm_sec;
rtc_raw.tm_min = rtc_tm.tm_min;
rtc_raw.tm_hour = rtc_tm.tm_hour;
rtc_raw.tm_mday = rtc_tm.tm_mday;
rtc_raw.tm_mon = rtc_tm.tm_mon;
rtc_raw.tm_year = rtc_tm.tm_year;
rtc_raw.tm_wday = rtc_tm.tm_wday;
rtc_raw.tm_yday = rtc_tm.tm_yday;
rtc_raw.tm_isdst = rtc_tm.tm_isdst;
rtc_from_t(&new_rtc_time, &rtc_raw, rtc_on_utc);
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
if (status < 0) {
@@ -750,16 +761,11 @@ process_reading(time_t rtc_time, struct timespec *system_time)
/* ================================================== */
static void
read_from_device(int fd_, int event, void *any)
int
RTC_Linux_CheckInterrupt(int fd)
{
int status;
unsigned long data;
struct timespec sys_time;
struct rtc_time rtc_raw;
struct tm rtc_tm;
time_t rtc_t;
int error = 0;
status = read(fd, &data, sizeof(data));
@@ -767,64 +773,79 @@ read_from_device(int fd_, int event, void *any)
/* This looks like a bad error : the file descriptor was indicating it was
* ready to read but we couldn't read anything. Give up. */
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
SCH_RemoveFileHandler(fd);
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
close(fd);
fd = -1;
return;
}
return -1;
}
if (skip_interrupts > 0) {
/* Wait for the next interrupt, this one may be bogus */
skip_interrupts--;
return 0;
}
/* Update interrupt detected? */
return (data & RTC_UF) == RTC_UF;
}
time_t
RTC_Linux_ReadTimeAfterInterrupt(int fd, int utc,
struct timespec *sys_time_cooked,
struct timespec *sys_time_raw)
{
int status;
struct rtc_time rtc_raw;
/* Read RTC time, sandwiched between two polls of the system clock
so we can bound any error */
SCH_GetLastEventTime(sys_time_cooked, NULL, sys_time_raw);
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status < 0) {
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
return -1;
}
/* Convert RTC time into a struct timespec */
return t_from_rtc(&rtc_raw, utc);
}
static void
read_from_device(int fd_, int event, void *any)
{
struct timespec sys_time;
int status, error = 0;
time_t rtc_t;
status = RTC_Linux_CheckInterrupt(fd);
if (status < 0) {
SCH_RemoveFileHandler(fd);
RTC_Linux_SwitchInterrupt(fd, 0); /* Likely to raise error too, but just to be sure... */
close(fd);
fd = -1;
return;
} else if (status == 0) {
/* Wait for the next interrupt, this one may be bogus */
return;
}
if ((data & RTC_UF) == RTC_UF) {
/* Update interrupt detected */
/* Read RTC time, sandwiched between two polls of the system clock
so we can bound any error. */
rtc_t = RTC_Linux_ReadTimeAfterInterrupt(fd, rtc_on_utc, &sys_time, NULL);
if (rtc_t == (time_t)-1) {
error = 1;
goto turn_off_interrupt;
}
SCH_GetLastEventTime(&sys_time, NULL, NULL);
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status < 0) {
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
error = 1;
goto turn_off_interrupt;
}
/* Convert RTC time into a struct timespec */
rtc_tm.tm_sec = rtc_raw.tm_sec;
rtc_tm.tm_min = rtc_raw.tm_min;
rtc_tm.tm_hour = rtc_raw.tm_hour;
rtc_tm.tm_mday = rtc_raw.tm_mday;
rtc_tm.tm_mon = rtc_raw.tm_mon;
rtc_tm.tm_year = rtc_raw.tm_year;
rtc_tm.tm_wday = 0;
rtc_t = t_from_rtc(&rtc_tm);
if (rtc_t == (time_t)(-1)) {
error = 1;
goto turn_off_interrupt;
}
process_reading(rtc_t, &sys_time);
if (n_samples < 4) {
measurement_period = LOWEST_MEASUREMENT_PERIOD;
} else if (n_samples < 6) {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
} else if (n_samples < 10) {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
} else if (n_samples < 14) {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
} else {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
}
process_reading(rtc_t, &sys_time);
if (n_samples < 4) {
measurement_period = LOWEST_MEASUREMENT_PERIOD;
} else if (n_samples < 6) {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
} else if (n_samples < 10) {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
} else if (n_samples < 14) {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
} else {
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
}
turn_off_interrupt:
@@ -836,7 +857,7 @@ turn_off_interrupt:
operating_mode = OM_NORMAL;
(after_init_hook)(after_init_hook_arg);
switch_interrupts(0);
RTC_Linux_SwitchInterrupt(fd, 0);
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
}
@@ -848,7 +869,7 @@ turn_off_interrupt:
DEBUG_LOG("Could not complete after trim relock due to errors");
operating_mode = OM_NORMAL;
switch_interrupts(0);
RTC_Linux_SwitchInterrupt(fd, 0);
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
}
@@ -856,7 +877,7 @@ turn_off_interrupt:
break;
case OM_NORMAL:
switch_interrupts(0);
RTC_Linux_SwitchInterrupt(fd, 0);
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
@@ -878,7 +899,7 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
operating_mode = OM_INITIAL;
timeout_id = 0;
switch_interrupts(1);
RTC_Linux_SwitchInterrupt(fd, 1);
}
/* ================================================== */
@@ -911,6 +932,33 @@ RTC_Linux_WriteParameters(void)
return(retval);
}
time_t
RTC_Linux_ReadTimeNow(int fd, int utc,
struct timespec *old_sys_cooked,
struct timespec *old_sys_raw)
{
struct rtc_time rtc_raw, rtc_raw_retry;
int status;
/* Retry reading the RTC until both read attempts give the same sec value.
This way the race condition is prevented that the RTC has updated itself
during the first read operation. */
do {
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status >= 0) {
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
}
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
/* Read system clock */
if (old_sys_raw)
LCL_ReadRawTime(old_sys_raw);
if (old_sys_cooked)
LCL_ReadCookedTime(old_sys_cooked, NULL);
return status >= 0 ? t_from_rtc(&rtc_raw, utc) : -1;
}
/* ================================================== */
/* Try to set the system clock from the RTC, in the same manner as
/sbin/hwclock -s would do. We're not as picky about OS version
@@ -920,12 +968,10 @@ RTC_Linux_WriteParameters(void)
int
RTC_Linux_TimePreInit(time_t driftfile_time)
{
int fd, status;
struct rtc_time rtc_raw, rtc_raw_retry;
struct tm rtc_tm;
time_t rtc_t;
double accumulated_error, sys_offset;
struct timespec new_sys_time, old_sys_time;
int fd;
coefs_file_name = CNF_GetRtcFile();
@@ -938,66 +984,41 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
return 0; /* Can't open it, and won't be able to later */
}
/* Retry reading the rtc until both read attempts give the same sec value.
This way the race condition is prevented that the RTC has updated itself
during the first read operation. */
do {
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status >= 0) {
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
}
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
/* Read system clock */
LCL_ReadCookedTime(&old_sys_time, NULL);
rtc_t = RTC_Linux_ReadTimeNow(fd, rtc_on_utc, &old_sys_time, NULL);
close(fd);
if (status >= 0) {
/* Convert to seconds since 1970 */
rtc_tm.tm_sec = rtc_raw.tm_sec;
rtc_tm.tm_min = rtc_raw.tm_min;
rtc_tm.tm_hour = rtc_raw.tm_hour;
rtc_tm.tm_mday = rtc_raw.tm_mday;
rtc_tm.tm_mon = rtc_raw.tm_mon;
rtc_tm.tm_year = rtc_raw.tm_year;
rtc_t = t_from_rtc(&rtc_tm);
if (rtc_t != (time_t)(-1)) {
if (rtc_t != (time_t)(-1)) {
/* Work out approximatation to correct time (to about the
nearest second) */
if (valid_coefs_from_file) {
accumulated_error = file_ref_offset +
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
} else {
accumulated_error = 0.0;
}
/* Correct time */
new_sys_time.tv_sec = rtc_t;
/* Average error in the RTC reading */
new_sys_time.tv_nsec = 500000000;
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
return 0;
}
sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) {
if (LCL_ApplyStepOffset(sys_offset))
LOG(LOGS_INFO, "System time set from RTC");
}
/* Work out approximation to correct time (to about the
nearest second) */
if (valid_coefs_from_file) {
accumulated_error = file_ref_offset +
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
} else {
accumulated_error = 0.0;
}
/* Correct time */
new_sys_time.tv_sec = rtc_t;
/* Average error in the RTC reading */
new_sys_time.tv_nsec = 500000000;
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
return 0;
}
sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) {
if (LCL_ApplyStepOffset(sys_offset))
LOG(LOGS_INFO, "System time set from RTC");
}
} else {
return 0;
}
@@ -1065,7 +1086,7 @@ RTC_Linux_Trim(void)
/* And start rapid sampling, interrupts on now */
SCH_RemoveTimeout(timeout_id);
timeout_id = 0;
switch_interrupts(1);
RTC_Linux_SwitchInterrupt(fd, 1);
}
return 1;

View File

@@ -42,4 +42,13 @@ extern int RTC_Linux_Trim(void);
extern void RTC_Linux_CycleLogFile(void);
extern int RTC_Linux_SwitchInterrupt(int fd, int on_off);
extern int RTC_Linux_CheckInterrupt(int fd);
extern time_t RTC_Linux_ReadTimeAfterInterrupt(int fd, int utc,
struct timespec *sys_time_cooked,
struct timespec *sys_time_raw);
extern time_t RTC_Linux_ReadTimeNow(int fd, int utc,
struct timespec *sys_time_cooked,
struct timespec *sys_time_raw);
#endif /* _GOT_RTC_LINUX_H */

View File

@@ -47,12 +47,6 @@ static int initialised = 0;
/* One more than the highest file descriptor that is registered */
static unsigned int one_highest_fd;
#ifndef FD_SETSIZE
/* If FD_SETSIZE is not defined, assume that fd_set is implemented
as a fixed size array of bits, possibly embedded inside a record */
#define FD_SETSIZE (sizeof(fd_set) * 8)
#endif
typedef struct {
SCH_FileHandler handler;
SCH_ArbitraryArgument arg;

View File

@@ -28,18 +28,14 @@
#include "sysincl.h"
#ifdef HAVE_NETTLE_SIV_CMAC
#include <nettle/siv-cmac.h>
#else
#include "siv_nettle_int.c"
#endif
#ifdef HAVE_NETTLE_SIV_GCM
#include <nettle/siv-gcm.h>
#endif
#include "memory.h"
#include "siv.h"
#include "util.h"
struct SIV_Instance_Record {
SIV_Algorithm algorithm;
@@ -163,8 +159,7 @@ SIV_GetMaxNonceLength(SIV_Instance instance)
int
SIV_GetTagLength(SIV_Instance instance)
{
if (instance->tag_length < 1 || instance->tag_length > SIV_MAX_TAG_LENGTH)
assert(0);
BRIEF_ASSERT(instance->tag_length >= 1 && instance->tag_length <= SIV_MAX_TAG_LENGTH);
return instance->tag_length;
}

View File

@@ -1,452 +0,0 @@
/* This is a single-file implementation of AES-SIV-CMAC-256 based on
a patch for GNU Nettle by Nikos Mavrogiannopoulos */
/*
AES-CMAC-128 (rfc 4493)
Copyright (C) Stefan Metzmacher 2012
Copyright (C) Jeremy Allison 2012
Copyright (C) Michael Adam 2012
Copyright (C) 2017, Red Hat Inc.
This file is part of GNU Nettle.
GNU Nettle is free software: you can redistribute it and/or
modify it under the terms of either:
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your
option) any later version.
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
or both in parallel, as here.
GNU Nettle 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 copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see http://www.gnu.org/licenses/.
*/
/* siv-aes128.c, siv-cmac.c, siv.h
AES-SIV, RFC5297
SIV-CMAC, RFC5297
Copyright (C) 2017 Nikos Mavrogiannopoulos
This file is part of GNU Nettle.
GNU Nettle is free software: you can redistribute it and/or
modify it under the terms of either:
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your
option) any later version.
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
or both in parallel, as here.
GNU Nettle 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 copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see http://www.gnu.org/licenses/.
*/
/* cmac.h, siv-cmac.h, cmac-aes128.c
CMAC mode, as specified in RFC4493
SIV-CMAC mode, as specified in RFC5297
CMAC using AES128 as the underlying cipher.
Copyright (C) 2017 Red Hat, Inc.
Contributed by Nikos Mavrogiannopoulos
This file is part of GNU Nettle.
GNU Nettle is free software: you can redistribute it and/or
modify it under the terms of either:
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your
option) any later version.
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
or both in parallel, as here.
GNU Nettle 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 copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see http://www.gnu.org/licenses/.
*/
# include "config.h"
#include <assert.h>
#include <string.h>
#include "nettle/aes.h"
#include "nettle/ctr.h"
#include "nettle/macros.h"
#include "nettle/memxor.h"
#include "nettle/memops.h"
#include "nettle/nettle-types.h"
/* For SIV, the block size of the block cipher shall be 128 bits. */
#define SIV_BLOCK_SIZE 16
#define SIV_DIGEST_SIZE 16
#define SIV_MIN_NONCE_SIZE 1
/*
* SIV mode requires the aad and plaintext when building the IV, which
* prevents streaming processing and it incompatible with the AEAD API.
*/
/* AES_SIV_CMAC_256 */
struct siv_cmac_aes128_ctx {
struct aes128_ctx cipher;
uint8_t s2vk[AES128_KEY_SIZE];
};
struct cmac128_ctx
{
/* Key */
union nettle_block16 K1;
union nettle_block16 K2;
/* MAC state */
union nettle_block16 X;
/* Block buffer */
union nettle_block16 block;
size_t index;
};
/* shift one and XOR with 0x87. */
static void
_cmac128_block_mulx(union nettle_block16 *dst,
const union nettle_block16 *src)
{
uint64_t b1 = READ_UINT64(src->b);
uint64_t b2 = READ_UINT64(src->b+8);
b1 = (b1 << 1) | (b2 >> 63);
b2 <<= 1;
if (src->b[0] & 0x80)
b2 ^= 0x87;
WRITE_UINT64(dst->b, b1);
WRITE_UINT64(dst->b+8, b2);
}
static void
cmac128_set_key(struct cmac128_ctx *ctx, const void *cipher,
nettle_cipher_func *encrypt)
{
static const uint8_t const_zero[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
union nettle_block16 *L = &ctx->block;
memset(ctx, 0, sizeof(*ctx));
/* step 1 - generate subkeys k1 and k2 */
encrypt(cipher, 16, L->b, const_zero);
_cmac128_block_mulx(&ctx->K1, L);
_cmac128_block_mulx(&ctx->K2, &ctx->K1);
}
#define MIN(x,y) ((x)<(y)?(x):(y))
static void
cmac128_update(struct cmac128_ctx *ctx, const void *cipher,
nettle_cipher_func *encrypt,
size_t msg_len, const uint8_t *msg)
{
union nettle_block16 Y;
/*
* check if we expand the block
*/
if (ctx->index < 16)
{
size_t len = MIN(16 - ctx->index, msg_len);
memcpy(&ctx->block.b[ctx->index], msg, len);
msg += len;
msg_len -= len;
ctx->index += len;
}
if (msg_len == 0) {
/* if it is still the last block, we are done */
return;
}
/*
* now checksum everything but the last block
*/
memxor3(Y.b, ctx->X.b, ctx->block.b, 16);
encrypt(cipher, 16, ctx->X.b, Y.b);
while (msg_len > 16)
{
memxor3(Y.b, ctx->X.b, msg, 16);
encrypt(cipher, 16, ctx->X.b, Y.b);
msg += 16;
msg_len -= 16;
}
/*
* copy the last block, it will be processed in
* cmac128_digest().
*/
memcpy(ctx->block.b, msg, msg_len);
ctx->index = msg_len;
}
static void
cmac128_digest(struct cmac128_ctx *ctx, const void *cipher,
nettle_cipher_func *encrypt,
unsigned length,
uint8_t *dst)
{
union nettle_block16 Y;
memset(ctx->block.b+ctx->index, 0, sizeof(ctx->block.b)-ctx->index);
/* re-use ctx->block for memxor output */
if (ctx->index < 16)
{
ctx->block.b[ctx->index] = 0x80;
memxor(ctx->block.b, ctx->K2.b, 16);
}
else
{
memxor(ctx->block.b, ctx->K1.b, 16);
}
memxor3(Y.b, ctx->block.b, ctx->X.b, 16);
assert(length <= 16);
if (length == 16)
{
encrypt(cipher, 16, dst, Y.b);
}
else
{
encrypt(cipher, 16, ctx->block.b, Y.b);
memcpy(dst, ctx->block.b, length);
}
/* reset state for re-use */
memset(&ctx->X, 0, sizeof(ctx->X));
ctx->index = 0;
}
#define CMAC128_CTX(type) \
{ struct cmac128_ctx ctx; type cipher; }
/* NOTE: Avoid using NULL, as we don't include anything defining it. */
#define CMAC128_SET_KEY(self, set_key, encrypt, cmac_key) \
do { \
(set_key)(&(self)->cipher, (cmac_key)); \
if (0) (encrypt)(&(self)->cipher, ~(size_t) 0, \
(uint8_t *) 0, (const uint8_t *) 0); \
cmac128_set_key(&(self)->ctx, &(self)->cipher, \
(nettle_cipher_func *) (encrypt)); \
} while (0)
#define CMAC128_UPDATE(self, encrypt, length, src) \
cmac128_update(&(self)->ctx, &(self)->cipher, \
(nettle_cipher_func *)encrypt, (length), (src))
#define CMAC128_DIGEST(self, encrypt, length, digest) \
(0 ? (encrypt)(&(self)->cipher, ~(size_t) 0, \
(uint8_t *) 0, (const uint8_t *) 0) \
: cmac128_digest(&(self)->ctx, &(self)->cipher, \
(nettle_cipher_func *) (encrypt), \
(length), (digest)))
struct cmac_aes128_ctx CMAC128_CTX(struct aes128_ctx);
static void
cmac_aes128_set_key(struct cmac_aes128_ctx *ctx, const uint8_t *key)
{
CMAC128_SET_KEY(ctx, aes128_set_encrypt_key, aes128_encrypt, key);
}
static void
cmac_aes128_update (struct cmac_aes128_ctx *ctx,
size_t length, const uint8_t *data)
{
CMAC128_UPDATE (ctx, aes128_encrypt, length, data);
}
static void
cmac_aes128_digest(struct cmac_aes128_ctx *ctx,
size_t length, uint8_t *digest)
{
CMAC128_DIGEST(ctx, aes128_encrypt, length, digest);
}
static const uint8_t const_one[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
};
static const uint8_t const_zero[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static
void _siv_s2v(nettle_set_key_func *cmac_set_key,
nettle_hash_update_func *cmac_update,
nettle_hash_digest_func *cmac_digest,
size_t cmac_ctx_size,
const uint8_t *s2vk, size_t alength, const uint8_t *adata,
size_t nlength, const uint8_t *nonce,
size_t plength, const uint8_t *pdata,
uint8_t *v)
{
uint8_t ctx[sizeof(struct cmac128_ctx)+sizeof(struct aes_ctx)];
union nettle_block16 D, S, T;
assert(cmac_ctx_size <= sizeof (ctx));
cmac_set_key(ctx, s2vk);
if (nlength == 0 && alength == 0) {
cmac_update(ctx, 16, const_one);
cmac_digest(ctx, 16, v);
return;
}
cmac_update(ctx, 16, const_zero);
cmac_digest(ctx, 16, D.b);
if (1) {
_cmac128_block_mulx(&D, &D);
cmac_update(ctx, alength, adata);
cmac_digest(ctx, 16, S.b);
memxor(D.b, S.b, 16);
}
if (nlength > 0) {
_cmac128_block_mulx(&D, &D);
cmac_update(ctx, nlength, nonce);
cmac_digest(ctx, 16, S.b);
memxor(D.b, S.b, 16);
}
/* Sn */
if (plength >= 16) {
cmac_update(ctx, plength-16, pdata);
pdata += plength-16;
memxor3(T.b, pdata, D.b, 16);
} else {
union nettle_block16 pad;
_cmac128_block_mulx(&T, &D);
memcpy(pad.b, pdata, plength);
pad.b[plength] = 0x80;
if (plength+1 < 16)
memset(&pad.b[plength+1], 0, 16-plength-1);
memxor(T.b, pad.b, 16);
}
cmac_update(ctx, 16, T.b);
cmac_digest(ctx, 16, v);
}
static void
siv_cmac_aes128_set_key(struct siv_cmac_aes128_ctx *ctx, const uint8_t *key)
{
memcpy(ctx->s2vk, key, 16);
aes128_set_encrypt_key(&ctx->cipher, key+16);
}
static void
siv_cmac_aes128_encrypt_message(struct siv_cmac_aes128_ctx *ctx,
size_t nlength, const uint8_t *nonce,
size_t alength, const uint8_t *adata,
size_t clength, uint8_t *dst, const uint8_t *src)
{
union nettle_block16 siv;
size_t slength;
assert (clength >= SIV_DIGEST_SIZE);
slength = clength - SIV_DIGEST_SIZE;
/* create CTR nonce */
_siv_s2v((nettle_set_key_func*)cmac_aes128_set_key,
(nettle_hash_update_func*)cmac_aes128_update,
(nettle_hash_digest_func*)cmac_aes128_digest,
sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata,
nlength, nonce, slength, src, siv.b);
memcpy(dst, siv.b, SIV_DIGEST_SIZE);
siv.b[8] &= ~0x80;
siv.b[12] &= ~0x80;
ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE,
siv.b, slength, dst+SIV_DIGEST_SIZE, src);
}
static int
siv_cmac_aes128_decrypt_message(struct siv_cmac_aes128_ctx *ctx,
size_t nlength, const uint8_t *nonce,
size_t alength, const uint8_t *adata,
size_t mlength, uint8_t *dst, const uint8_t *src)
{
union nettle_block16 siv;
union nettle_block16 ctr;
memcpy(ctr.b, src, SIV_DIGEST_SIZE);
ctr.b[8] &= ~0x80;
ctr.b[12] &= ~0x80;
ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE,
ctr.b, mlength, dst, src+SIV_DIGEST_SIZE);
/* create CTR nonce */
_siv_s2v((nettle_set_key_func*)cmac_aes128_set_key,
(nettle_hash_update_func*)cmac_aes128_update,
(nettle_hash_digest_func*)cmac_aes128_digest,
sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata,
nlength, nonce, mlength, dst, siv.b);
return memeql_sec(siv.b, src, SIV_DIGEST_SIZE);
}

View File

@@ -213,12 +213,25 @@ get_reusable_socket(int type, IPSockAddr *spec)
/* ================================================== */
#if defined(SOCK_CLOEXEC) || defined(SOCK_NONBLOCK)
static int
get_default_inet_domain(void)
{
#ifdef FEAT_IPV6
if (!ip4_enabled && ip6_enabled)
return AF_INET6;
#endif
return AF_INET;
}
/* ================================================== */
static int
check_socket_flag(int sock_flag, int fd_flag, int fs_flag)
{
int sock_fd, fd_flags, fs_flags;
sock_fd = socket(AF_INET, SOCK_DGRAM | sock_flag, 0);
sock_fd = socket(get_default_inet_domain(), SOCK_DGRAM | sock_flag, 0);
if (sock_fd < 0)
return 0;
@@ -526,7 +539,7 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *ifac
else if (remote_addr)
family = remote_addr->ip_addr.family;
else
family = IPADDR_INET4;
family = !ip4_enabled && ip6_enabled ? IPADDR_INET6 : IPADDR_INET4;
switch (family) {
case IPADDR_INET4:
@@ -590,35 +603,44 @@ error:
/* ================================================== */
static socklen_t
set_unix_sockaddr(struct sockaddr_un *sa, const char *addr)
{
size_t len = strlen(addr);
if (len + 1 > sizeof (sa->sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr);
return 0;
}
memset(sa, 0, sizeof (*sa));
sa->sun_family = AF_UNIX;
memcpy(sa->sun_path, addr, len);
return offsetof(struct sockaddr_un, sun_path) + len + 1;
}
/* ================================================== */
static int
bind_unix_address(int sock_fd, const char *addr, int flags)
{
union sockaddr_all saddr;
socklen_t saddr_len;
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);
saddr_len = set_unix_sockaddr(&saddr.un, addr);
if (saddr_len == 0)
return 0;
}
saddr.un.sun_family = AF_UNIX;
if (unlink(addr) < 0)
DEBUG_LOG("Could not remove %s : %s", addr, strerror(errno));
/* PRV_BindSocket() doesn't support Unix sockets yet */
if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
if (bind(sock_fd, &saddr.sa, saddr_len) < 0) {
DEBUG_LOG("Could not bind Unix socket to %s : %s", addr, strerror(errno));
return 0;
}
/* Allow access to everyone with access to the directory if requested */
if (flags & SCK_FLAG_ALL_PERMISSIONS && chmod(addr, 0666) < 0) {
DEBUG_LOG("Could not change permissions of %s : %s", addr, strerror(errno));
return 0;
}
return 1;
}
@@ -628,17 +650,13 @@ static int
connect_unix_address(int sock_fd, const char *addr)
{
union sockaddr_all saddr;
socklen_t saddr_len;
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);
saddr_len = set_unix_sockaddr(&saddr.un, addr);
if (saddr_len == 0)
return 0;
}
saddr.un.sun_family = AF_UNIX;
if (connect(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
if (connect(sock_fd, &saddr.sa, saddr_len) < 0) {
DEBUG_LOG("Could not connect Unix socket to %s : %s", addr, strerror(errno));
return 0;
}
@@ -853,8 +871,10 @@ static int
process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
SCK_Message *message)
{
int r = 1, path_len, max_path_len;
struct cmsghdr *cmsg;
int r = 1;
init_message_addresses(message, SCK_ADDR_UNSPEC);
if (msg->msg_namelen <= sizeof (union sockaddr_all) &&
msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
@@ -867,18 +887,23 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
SCK_SockaddrToIPSockAddr(msg->msg_name, msg->msg_namelen, &message->remote_addr.ip);
break;
case AF_UNIX:
/* Make sure the path is terminated by '\0' */
max_path_len = sizeof (((struct sockaddr_un *)msg->msg_name)->sun_path);
path_len = strnlen(((struct sockaddr_un *)msg->msg_name)->sun_path, max_path_len);
if (path_len >= max_path_len) {
DEBUG_LOG("Unterminated path");
r = 0;
break;
}
init_message_addresses(message, SCK_ADDR_UNIX);
message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path;
break;
default:
init_message_addresses(message, SCK_ADDR_UNSPEC);
DEBUG_LOG("Unexpected address");
r = 0;
break;
}
} else {
init_message_addresses(message, SCK_ADDR_UNSPEC);
if (msg->msg_namelen > sizeof (union sockaddr_all)) {
DEBUG_LOG("Truncated source address");
r = 0;
@@ -1050,9 +1075,8 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
n = ARR_GetSize(recv_headers);
n = MIN(n, max_messages);
if (n < 1 || n > MAX_RECV_MESSAGES ||
n > ARR_GetSize(recv_messages) || n > ARR_GetSize(recv_sck_messages))
assert(0);
BRIEF_ASSERT(n >= 1 && n <= MAX_RECV_MESSAGES &&
n <= ARR_GetSize(recv_messages) && n <= ARR_GetSize(recv_sck_messages));
recv_flags = get_recv_flags(flags);
@@ -1142,14 +1166,9 @@ send_message(int sock_fd, SCK_Message *message, int flags)
(struct sockaddr *)&saddr, sizeof (saddr));
break;
case SCK_ADDR_UNIX:
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s",
message->remote_addr.path) >= sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", message->remote_addr.path);
saddr_len = set_unix_sockaddr(&saddr.un, message->remote_addr.path);
if (saddr_len == 0)
return 0;
}
saddr.un.sun_family = AF_UNIX;
saddr_len = sizeof (saddr.un);
break;
default:
assert(0);

View File

@@ -34,8 +34,7 @@
#define SCK_FLAG_BLOCK 1
#define SCK_FLAG_BROADCAST 2
#define SCK_FLAG_RX_DEST_ADDR 4
#define SCK_FLAG_ALL_PERMISSIONS 8
#define SCK_FLAG_PRIV_BIND 16
#define SCK_FLAG_PRIV_BIND 8
/* Flags for receiving and sending messages */
#define SCK_FLAG_MSG_ERRQUEUE 1

235
sources.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2024
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2025
*
* 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
@@ -54,7 +54,6 @@ static int initialised = 0;
/* ================================================== */
/* Structure used to hold info for selecting between sources */
struct SelectInfo {
int select_ok;
double std_dev;
double root_distance;
double lo_limit;
@@ -66,10 +65,11 @@ struct SelectInfo {
/* This enum contains the flag values that are used to label
each source */
typedef enum {
SRC_OK, /* OK so far, not a final status! */
SRC_OK = 0, /* OK so far, not a final status! */
SRC_UNSELECTABLE, /* Has noselect option set */
SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_UNSYNCHRONISED, /* Provides samples, but not synchronised */
SRC_BAD_STRATUM, /* Has stratum outside of allowed range */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
@@ -106,13 +106,20 @@ struct SRC_Instance_Record {
/* Number of set bits in the reachability register */
int reachability_size;
/* Updates since last reference update */
/* Number of reachability updates with cleared register */
int unreachable_run;
/* Maximum number of reachability updates with cleared register to still
allow selection */
int max_unreachable_run;
/* Number of selection updates since last reference update */
int updates;
/* Updates left before allowing combining */
/* Number of selection updates left before allowing combining again */
int distant;
/* Updates with a status requiring source replacement */
/* Number of selection updates with a status requiring source replacement */
int bad;
/* Flag indicating the status of the source */
@@ -144,9 +151,9 @@ 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;
/* Flags indicating which status was already reported for
the source since the last change of the system peer */
char reported_status[SRC_SELECTED + 1];
};
/* ================================================== */
@@ -191,6 +198,8 @@ static int forced_first_report; /* Flag to allow one failed selection to be
static double max_distance;
static double max_jitter;
static int max_stratum;
static int min_stratum;
static double reselect_distance;
static double stratum_weight;
static double combine_limit;
@@ -206,8 +215,7 @@ static LOG_FileID logfileid;
/* Forward prototype */
static void update_sel_options(void);
static void unselect_selected_source(LOG_Severity severity, const char *format,
const char *arg);
static void unselect_selected_source(LOG_Severity severity, const char *format, ...);
static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything);
@@ -225,6 +233,8 @@ void SRC_Initialise(void) {
selected_source_index = INVALID_SOURCE;
max_distance = CNF_GetMaxDistance();
max_jitter = CNF_GetMaxJitter();
max_stratum = CNF_GetMaxStratum();
min_stratum = CNF_GetMinStratum();
reselect_distance = CNF_GetReselectDistance();
stratum_weight = CNF_GetStratumWeight();
combine_limit = CNF_GetCombineLimit();
@@ -260,7 +270,8 @@ void SRC_Finalise(void)
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
int sel_options, IPAddr *addr, int min_samples,
int max_samples, double min_delay, double asymmetry)
int max_samples, double min_delay, double asymmetry,
int max_unreach)
{
SRC_Instance result;
@@ -296,6 +307,7 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authentic
result->authenticated = authenticated;
result->conf_sel_options = sel_options;
result->sel_options = sel_options;
result->max_unreachable_run = max_unreach;
result->active = 0;
SRC_SetRefid(result, ref_id, addr);
@@ -322,9 +334,8 @@ void SRC_DestroyInstance(SRC_Instance instance)
last_updated_inst = NULL;
assert(initialised);
if (instance->index < 0 || instance->index >= n_sources ||
instance != sources[instance->index])
assert(0);
BRIEF_ASSERT(instance->index >= 0 && instance->index < n_sources &&
instance == sources[instance->index]);
SST_DeleteInstance(instance->stats);
dead_index = instance->index;
@@ -340,7 +351,7 @@ void SRC_DestroyInstance(SRC_Instance instance)
if (selected_source_index > dead_index)
--selected_source_index;
else if (selected_source_index == dead_index)
unselect_selected_source(LOGS_INFO, NULL, NULL);
unselect_selected_source(LOGS_INFO, NULL);
SRC_SelectSource(NULL);
}
@@ -353,6 +364,7 @@ SRC_ResetInstance(SRC_Instance instance)
instance->updates = 0;
instance->reachability = 0;
instance->reachability_size = 0;
instance->unreachable_run = 0;
instance->distant = 0;
instance->bad = 0;
instance->status = SRC_BAD_STATS;
@@ -360,8 +372,8 @@ SRC_ResetInstance(SRC_Instance instance)
instance->stratum = 0;
instance->leap = LEAP_Unsynchronised;
instance->leap_vote = 0;
instance->reported_falseticker = 0;
memset(instance->reported_status, 0, sizeof (instance->reported_status));
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
SST_ResetInstance(instance->stats);
@@ -526,6 +538,13 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++;
if (inst->reachability == 0) {
if (inst->unreachable_run < INT_MAX)
inst->unreachable_run++;
} else {
inst->unreachable_run = 0;
}
/* Check if special reference update mode failed */
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
REF_SetUnsynchronised();
@@ -547,6 +566,7 @@ SRC_ResetReachability(SRC_Instance inst)
{
inst->reachability = 0;
inst->reachability_size = 0;
inst->unreachable_run = 0;
SRC_UpdateReachability(inst, 0);
}
@@ -620,20 +640,45 @@ update_sel_options(void)
/* ================================================== */
FORMAT_ATTRIBUTE_PRINTF(2, 3)
static void
log_selection_message(LOG_Severity severity, const char *format, const char *arg)
log_selection_message(LOG_Severity severity, const char *format, ...)
{
char buf[256];
va_list ap;
if (REF_GetMode() != REF_ModeNormal)
return;
LOG(severity, format, arg);
va_start(ap, format);
vsnprintf(buf, sizeof (buf), format, ap);
va_end(ap);
LOG(severity, "%s", buf);
}
/* ================================================== */
FORMAT_ATTRIBUTE_PRINTF(3, 4)
static void
log_selection_source(LOG_Severity severity, const char *format, SRC_Instance inst)
log_selection_source(LOG_Severity severity, SRC_Instance inst, const char *format, ...)
{
char buf[320], *name, *ntp_name;
char buf[320], buf2[256], *name, *ntp_name, *s;
va_list ap;
if (REF_GetMode() != REF_ModeNormal)
return;
va_start(ap, format);
vsnprintf(buf2, sizeof (buf2), format, ap);
va_end(ap);
/* Replace ## with %s in the formatted string to be the source name */
s = strstr(buf2, "##");
if (!s || strchr(buf2, '%'))
return;
s[0] = '%';
s[1] = 's';
name = source_to_string(inst);
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
@@ -643,7 +688,9 @@ log_selection_source(LOG_Severity severity, const char *format, SRC_Instance ins
else
snprintf(buf, sizeof (buf), "%s", name);
log_selection_message(severity, format, buf);
LOG(severity, buf2, buf);
inst->reported_status[inst->status] = 1;
}
/* ================================================== */
@@ -686,7 +733,7 @@ source_to_string(SRC_Instance inst)
/* ================================================== */
static void
mark_source(SRC_Instance inst, SRC_Status status)
set_source_status(SRC_Instance inst, SRC_Status status)
{
struct timespec now;
@@ -696,7 +743,8 @@ mark_source(SRC_Instance inst, SRC_Status status)
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))
(status == SRC_FALSETICKER || status == SRC_BAD_DISTANCE ||
status == SRC_BAD_STRATUM || status == SRC_JITTERY))
inst->bad++;
else
inst->bad = 0;
@@ -731,6 +779,51 @@ mark_source(SRC_Instance inst, SRC_Status status)
/* ================================================== */
static void
mark_source(SRC_Instance inst, SRC_Status status)
{
set_source_status(inst, status);
BRIEF_ASSERT(status >= SRC_OK && status < sizeof (inst->reported_status));
if (!inst->reported_status[status]) {
switch (status) {
case SRC_BAD_STRATUM:
if (inst->bad < BAD_HANDLE_THRESHOLD)
break;
log_selection_source(LOGS_WARN, inst,
"Stratum of ## %sstratum of %d",
inst->stratum < min_stratum ? "below min" : "above max",
inst->stratum < min_stratum ? min_stratum : max_stratum);
break;
case SRC_BAD_DISTANCE:
if (inst->bad < BAD_HANDLE_THRESHOLD)
break;
log_selection_source(LOGS_WARN, inst,
"Root distance of ## exceeds maxdistance of %.3f seconds",
max_distance);
break;
case SRC_JITTERY:
if (inst->bad < BAD_HANDLE_THRESHOLD)
break;
log_selection_source(LOGS_WARN, inst,
"Jitter of ## exceeds maxjitter of %.3f seconds",
max_jitter);
break;
case SRC_FALSETICKER:
log_selection_source(LOGS_WARN, inst, "Detected falseticker ##");
break;
case SRC_SELECTED:
log_selection_source(LOGS_INFO, inst, "Selected source ##");
break;
default:
break;
}
}
}
/* ================================================== */
static void
mark_ok_sources(SRC_Status status)
{
@@ -739,7 +832,8 @@ mark_ok_sources(SRC_Status status)
for (i = 0; i < n_sources; i++) {
if (sources[i]->status != SRC_OK)
continue;
mark_source(sources[i], status);
/* Don't log the status in this case (multiple sources at once) */
set_source_status(sources[i], status);
}
}
@@ -749,16 +843,24 @@ mark_ok_sources(SRC_Status status)
call providing a message or selection of another source, which resets the
report_selection_loss flag. */
FORMAT_ATTRIBUTE_PRINTF(2, 3)
static void
unselect_selected_source(LOG_Severity severity, const char *format, const char *arg)
unselect_selected_source(LOG_Severity severity, const char *format, ...)
{
char buf[256];
va_list ap;
if (selected_source_index != INVALID_SOURCE) {
selected_source_index = INVALID_SOURCE;
report_selection_loss = 1;
}
if (report_selection_loss && format) {
log_selection_message(severity, format, arg);
va_start(ap, format);
vsnprintf(buf, sizeof (buf), format, ap);
va_end(ap);
log_selection_message(severity, "%s", buf);
report_selection_loss = 0;
}
}
@@ -864,9 +966,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
int max_badstat_reach, max_badstat_reach_size, n_badstats_sources;
int max_sel_reach, max_sel_reach_size;
int max_sel_reach, max_sel_reach_size, n_unreach_sources;
int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources;
int combined, stratum, min_stratum, max_score_index;
int combined, stratum, min_sel_stratum, max_score_index;
int orphan_stratum, orphan_source;
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion;
@@ -881,7 +983,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
if (n_sources == 0) {
unselect_selected_source(LOGS_INFO, "Can't synchronise: no sources", NULL);
unselect_selected_source(LOGS_WARN, "Can't synchronise: no sources");
return;
}
@@ -893,6 +995,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints = 0;
n_sel_sources = n_sel_trust_sources = 0;
n_badstats_sources = 0;
n_unreach_sources = 0;
sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0;
max_sel_reach_size = max_badstat_reach_size = 0;
@@ -915,13 +1018,15 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now,
&si->lo_limit, &si->hi_limit, &si->root_distance,
&si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok);
/* Count unreachable sources for a warning message */
if (sources[i]->reachability == 0)
n_unreach_sources++;
if (!si->select_ok) {
si = &sources[i]->sel_info;
if (!SST_GetSelectionData(sources[i]->stats, &now, &si->lo_limit, &si->hi_limit,
&si->root_distance, &si->std_dev, &first_sample_ago,
&si->last_sample_ago)) {
++n_badstats_sources;
mark_source(sources[i], SRC_BAD_STATS);
if (max_badstat_reach < sources[i]->reachability)
@@ -937,6 +1042,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* Require the stratum to be in the allowed range */
if (sources[i]->stratum < min_stratum || sources[i]->stratum > max_stratum) {
mark_source(sources[i], SRC_BAD_STRATUM);
continue;
}
/* Include extra dispersion in the root distance of sources that don't
have new samples (the last sample is older than span of all samples) */
if (first_sample_ago < 2.0 * si->last_sample_ago) {
@@ -983,8 +1094,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Reachability is not a requirement for selection. An unreachable source
can still be selected if its newest sample is not older than the oldest
sample from reachable sources. */
if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) {
sample from reachable sources and the number of consecutive unreachable
updates does not exceed the configured maximum. */
if (sources[i]->reachability == 0 &&
(si->last_sample_ago > max_reach_sample_ago ||
sources[i]->unreachable_run > sources[i]->max_unreachable_run)) {
mark_source(sources[i], SRC_STALE);
continue;
}
@@ -1068,7 +1182,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE &&
max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
mark_ok_sources(SRC_WAITS_STATS);
unselect_selected_source(LOGS_INFO, NULL, NULL);
unselect_selected_source(LOGS_INFO, NULL);
return;
}
@@ -1082,7 +1196,8 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (n_endpoints == 0) {
/* No sources provided valid endpoints */
unselect_selected_source(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
unselect_selected_source(LOGS_WARN, "Can't synchronise: no selectable sources"
" (%d unreachable sources)", n_unreach_sources);
return;
}
@@ -1156,12 +1271,23 @@ SRC_SelectSource(SRC_Instance updated_inst)
assert(depth == 0 && trust_depth == 0);
assert(2 * n_sel_sources == n_endpoints);
if ((best_trust_depth == 0 && best_depth <= n_sel_sources / 2) ||
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
if (best_trust_depth > 0) {
best_depth = best_trust_depth;
n_sel_sources = n_sel_trust_sources;
}
if (best_depth <= n_sel_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);
if (best_depth < 2)
log_selection_message(LOGS_WARN, "%s (no agreement among %d %ssources)",
"Can't synchronise: no majority", n_sel_sources,
best_trust_depth > 0 ? "trusted " : "");
else
log_selection_message(LOGS_WARN, "%s (only %d of %d %ssources agree)",
"Can't synchronise: no majority", best_depth,
n_sel_sources, best_trust_depth > 0 ? "trusted " : "");
reported_no_majority = 1;
report_selection_loss = 0;
}
@@ -1212,15 +1338,11 @@ 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()) {
unselect_selected_source(LOGS_INFO, "Can't synchronise: %s selectable sources",
unselect_selected_source(LOGS_WARN, "Can't synchronise: %s selectable sources",
!n_sel_sources ? "no" :
sel_req_source ? "no required source in" : "not enough");
mark_ok_sources(SRC_WAITS_SOURCES);
@@ -1259,12 +1381,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Find minimum stratum */
index = sel_sources[0];
min_stratum = sources[index]->stratum;
min_sel_stratum = sources[index]->stratum;
for (i = 1; i < n_sel_sources; i++) {
index = sel_sources[i];
stratum = sources[index]->stratum;
if (stratum < min_stratum)
min_stratum = stratum;
if (min_sel_stratum > stratum)
min_sel_stratum = stratum;
}
/* Update scores and find the source with maximum score */
@@ -1275,7 +1397,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (selected_source_index != INVALID_SOURCE)
sel_src_distance = sources[selected_source_index]->sel_info.root_distance +
(sources[selected_source_index]->stratum - min_stratum) * stratum_weight;
(sources[selected_source_index]->stratum - min_sel_stratum) * stratum_weight;
for (i = 0; i < n_sources; i++) {
/* Reset score for non-selectable sources */
@@ -1287,7 +1409,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
distance = sources[i]->sel_info.root_distance +
(sources[i]->stratum - min_stratum) * stratum_weight;
(sources[i]->stratum - min_sel_stratum) * stratum_weight;
if (sources[i]->type == SRC_NTP)
distance += reselect_distance;
@@ -1329,19 +1451,18 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Before selecting the new synchronisation source wait until the reference
can be updated */
if (sources[max_score_index]->updates == 0) {
unselect_selected_source(LOGS_INFO, NULL, NULL);
unselect_selected_source(LOGS_INFO, NULL);
mark_ok_sources(SRC_WAITS_UPDATE);
return;
}
selected_source_index = max_score_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;
memset(sources[i]->reported_status, 0, sizeof (sources[i]->reported_status));
}
reported_no_majority = 0;
@@ -1813,6 +1934,8 @@ get_status_char(SRC_Status status)
return 'M';
case SRC_UNSYNCHRONISED:
return 's';
case SRC_BAD_STRATUM:
return 'r';
case SRC_BAD_DISTANCE:
return 'd';
case SRC_JITTERY:

View File

@@ -69,7 +69,8 @@ typedef enum {
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
int sel_options, IPAddr *addr, int min_samples,
int max_samples, double min_delay, double asymmetry);
int max_samples, double min_delay, double asymmetry,
int max_unreach);
/* Function to get rid of a source when it is being unconfigured.
This may cause the current reference source to be reselected, if this

View File

@@ -644,22 +644,16 @@ SST_GetFrequencyRange(SST_Stats inst,
/* ================================================== */
void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
double *std_dev,
double *first_sample_ago,
double *last_sample_ago,
int *select_ok)
int
SST_GetSelectionData(SST_Stats inst, struct timespec *now, double *offset_lo_limit,
double *offset_hi_limit, double *root_distance, double *std_dev,
double *first_sample_ago, double *last_sample_ago)
{
double offset, sample_elapsed;
int i, j;
if (!inst->n_samples) {
*select_ok = 0;
return;
return 0;
}
i = get_runsbuf_index(inst, inst->best_single_sample);
@@ -675,38 +669,25 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
*offset_lo_limit = offset - *root_distance;
*offset_hi_limit = offset + *root_distance;
#if 0
double average_offset, elapsed;
int average_ok;
/* average_ok ignored for now */
elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed;
if (fabs(average_offset - offset) <=
inst->peer_dispersions[j] + 0.5 * inst->peer_delays[i]) {
average_ok = 1;
} else {
average_ok = 0;
}
#endif
i = get_runsbuf_index(inst, 0);
*first_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
i = get_runsbuf_index(inst, inst->n_samples - 1);
*last_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
*select_ok = inst->regression_ok;
/* 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 < MIN_SAMPLES_FOR_REGRESS &&
if (!inst->regression_ok && inst->n_samples < MIN_SAMPLES_FOR_REGRESS &&
inst->n_samples == inst->max_samples) {
*std_dev = CNF_GetMaxJitter();
*select_ok = 1;
} else if (!inst->regression_ok) {
return 0;
}
DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f",
inst->n_samples, offset, *root_distance, *std_dev,
*first_sample_ago, *last_sample_ago, *select_ok);
*first_sample_ago, *last_sample_ago);
return 1;
}
/* ================================================== */

View File

@@ -67,15 +67,10 @@ extern void SST_DoNewRegression(SST_Stats inst);
extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */
extern void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
double *variance,
double *first_sample_ago,
double *last_sample_ago,
int *select_ok);
extern int SST_GetSelectionData(SST_Stats inst, struct timespec *now,
double *offset_lo_limit, double *offset_hi_limit,
double *root_distance, double *variance,
double *first_sample_ago, double *last_sample_ago);
/* Get data needed when setting up tracking on this source */
extern void

View File

@@ -49,6 +49,7 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
int max_unreach;
int filter_length;
int interleaved;
int sel_options;
@@ -79,6 +80,7 @@ typedef struct {
#define SRC_DEFAULT_MAXSOURCES 4
#define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1)
#define SRC_DEFAULT_MAXUNREACH 100000
#define SRC_DEFAULT_ASYMMETRY 1.0
#define SRC_DEFAULT_NTSPORT 4460
#define SRC_DEFAULT_CERTSET 0

313
stubs.c
View File

@@ -49,66 +49,6 @@
#include "sched.h"
#include "util.h"
#if defined(FEAT_NTP) && !defined(FEAT_ASYNCDNS)
/* This is a blocking implementation used when asynchronous resolving is not available */
struct DNS_Async_Instance {
const char *name;
DNS_NameResolveHandler handler;
void *arg;
int pipe[2];
};
static void
resolve_name(int fd, int event, void *anything)
{
struct DNS_Async_Instance *inst;
IPAddr addrs[DNS_MAX_ADDRESSES];
DNS_Status status;
int i;
inst = (struct DNS_Async_Instance *)anything;
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES);
for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES &&
addrs[i].family != IPADDR_UNSPEC; i++)
;
(inst->handler)(status, i, addrs, inst->arg);
Free(inst);
}
void
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
{
struct DNS_Async_Instance *inst;
inst = MallocNew(struct DNS_Async_Instance);
inst->name = name;
inst->handler = handler;
inst->arg = anything;
if (pipe(inst->pipe))
LOG_FATAL("pipe() failed");
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, resolve_name, inst);
if (write(inst->pipe[1], "", 1) < 0)
;
}
#endif /* !FEAT_ASYNCDNS */
#ifndef FEAT_CMDMON
void
@@ -144,259 +84,6 @@ MNL_Finalise(void)
#endif /* !FEAT_CMDMON */
#ifndef FEAT_NTP
void
NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval)
{
}
void
NCR_Initialise(void)
{
}
void
NCR_Finalise(void)
{
}
int
NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
{
return 1;
}
int
NCR_CheckAccessRestriction(IPAddr *ip_addr)
{
return 0;
}
void
NIO_Initialise(void)
{
}
void
NIO_Finalise(void)
{
}
void
NSR_Initialise(void)
{
}
void
NSR_Finalise(void)
{
}
NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
return NSR_TooManySources;
}
NSR_Status
NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
return NSR_TooManySources;
}
const char *
NSR_StatusToString(NSR_Status status)
{
return "NTP not supported";
}
NSR_Status
NSR_RemoveSource(IPAddr *address)
{
return NSR_NoSuchSource;
}
void
NSR_RemoveSourcesById(uint32_t conf_id)
{
}
void
NSR_RemoveAllSources(void)
{
}
void
NSR_HandleBadSource(IPAddr *address)
{
}
void
NSR_RefreshAddresses(void)
{
}
char *
NSR_GetName(IPAddr *address)
{
return NULL;
}
void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{
if (handler)
(handler)();
}
void
NSR_ResolveSources(void)
{
}
void NSR_StartSources(void)
{
}
void NSR_AutoStartSources(void)
{
}
int
NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
IPAddr *mask, IPAddr *address)
{
return 0;
}
uint32_t
NSR_GetLocalRefid(IPAddr *address)
{
return 0;
}
int
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
return 0;
}
int
NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
{
return 0;
}
int
NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
{
return 0;
}
int
NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
{
return 0;
}
int
NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
{
return 0;
}
int
NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
{
return 0;
}
int
NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
{
return 0;
}
int
NSR_ModifyOffset(IPAddr *address, double new_offset)
{
return 0;
}
int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{
return 0;
}
void
NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
memset(report, 0, sizeof (*report));
}
int
NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
{
return 0;
}
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
return 0;
}
void
NSR_GetActivityReport(RPT_ActivityReport *report)
{
memset(report, 0, sizeof (*report));
}
void
NSR_DumpAuthData(void)
{
}
#ifndef FEAT_CMDMON
void
CLG_Initialise(void)
{
}
void
CLG_Finalise(void)
{
}
void
DNS_SetAddressFamily(int family)
{
}
DNS_Status
DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
{
return DNS_Failure;
}
void
KEY_Initialise(void)
{
}
void
KEY_Finalise(void)
{
}
#endif /* !FEAT_CMDMON */
#endif /* !FEAT_NTP */
#ifndef FEAT_REFCLOCK
void
RCL_Initialise(void)

View File

@@ -5,6 +5,7 @@
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
* Copyright (C) Shachar Raindel 2025
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -38,6 +39,12 @@
#include <poll.h>
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
#include <linux/sockios.h>
#include <linux/ethtool.h>
#include <net/if.h>
#endif
#ifdef FEAT_SCFILTER
#include <sys/prctl.h>
#include <seccomp.h>
@@ -48,9 +55,6 @@
#ifdef FEAT_RTC
#include <linux/rtc.h>
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
#include <linux/sockios.h>
#endif
#endif
#ifdef FEAT_PRIVDROP
@@ -59,11 +63,13 @@
#endif
#include "sys_linux.h"
#include "sys_linux_scmp.h"
#include "sys_timex.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "privops.h"
#include "socket.h"
#include "util.h"
/* Frequency scale to convert from ppm to the timex freq */
@@ -91,9 +97,6 @@ static int max_tick_bias;
static int hz;
static double dhz; /* And dbl prec version of same for arithmetic */
/* Flag indicating whether adjtimex() can step the clock */
static int have_setoffset;
/* The assumed rate at which the effective frequency and tick values are
updated in the kernel */
static int tick_update_hz;
@@ -293,16 +296,11 @@ get_version_specific_details(void)
get_kernel_version(&major, &minor, &patch);
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
LOG_FATAL("Kernel version not supported, sorry.");
}
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
/* In tickless kernels before 2.6.33 the frequency is updated in
a half-second interval */
tick_update_hz = 2;
} else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
/* In kernels before 4.19 the frequency is updated only on internal ticks
(CONFIG_HZ). As their rate cannot be reliably detected from the user
space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
@@ -310,13 +308,6 @@ get_version_specific_details(void)
tick_update_hz = 100;
}
/* ADJ_SETOFFSET support */
if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
have_setoffset = 0;
} else {
have_setoffset = 1;
}
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
hz, nominal_tick, max_tick_bias, tick_update_hz);
}
@@ -337,34 +328,6 @@ reset_adjtime_offset(void)
/* ================================================== */
static int
test_step_offset(void)
{
struct timex txc;
/* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
This seems to be the only way how to verify that the kernel really
supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
mode. */
txc.modes = MOD_MAXERROR;
txc.maxerror = 0;
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror != 0)
return 0;
txc.modes = ADJ_SETOFFSET | ADJ_NANO;
txc.time.tv_sec = 0;
txc.time.tv_usec = 0;
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror < 100000)
return 0;
return 1;
}
/* ================================================== */
static void
report_time_adjust_blockers(void)
{
@@ -387,15 +350,10 @@ SYS_Linux_Initialise(void)
reset_adjtime_offset();
if (have_setoffset && !test_step_offset()) {
LOG(LOGS_INFO, "adjtimex() doesn't support ADJ_SETOFFSET");
have_setoffset = 0;
}
SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
1.0 / tick_update_hz,
read_frequency, set_frequency,
have_setoffset ? apply_step_offset : NULL,
apply_step_offset,
0.0, 0.0, NULL, NULL);
}
@@ -771,6 +729,14 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_A1(SCMP_CMP_EQ, ioctls[i])) < 0)
goto add_failed;
}
/* Allow selected ioctls that need to be specified in a separate
file to avoid conflicting headers (e.g. TCGETS2) */
for (i = 0; SYS_Linux_GetExtraScmpIoctl(i) != 0; i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
SCMP_A1(SCMP_CMP_EQ, SYS_Linux_GetExtraScmpIoctl(i))) < 0)
goto add_failed;
}
}
if (seccomp_load(ctx) < 0)
@@ -902,33 +868,99 @@ get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
/* ================================================== */
int
SYS_Linux_OpenPHC(const char *path, int phc_index)
/* Make sure an FD is a PHC. Return the FD if it is, or close the FD
and return -1 if it is not. */
static int
verify_fd_is_phc(int phc_fd)
{
struct ptp_clock_caps caps;
char phc_path[64];
int phc_fd;
if (!path) {
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
return -1;
path = phc_path;
}
phc_fd = open(path, O_RDONLY);
if (phc_fd < 0) {
LOG(LOGS_ERR, "Could not open %s : %s", path, strerror(errno));
return -1;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG(LOGS_ERR, "ioctl(%s) failed : %s", "PTP_CLOCK_GETCAPS", strerror(errno));
close(phc_fd);
return -1;
}
UTI_FdSetCloexec(phc_fd);
return phc_fd;
}
/* ================================================== */
static int
open_phc_by_iface_name(const char *iface, int flags)
{
#ifdef HAVE_LINUX_TIMESTAMPING
struct ethtool_ts_info ts_info;
char phc_device[PATH_MAX];
struct ifreq req;
int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return -1;
memset(&req, 0, sizeof (req));
memset(&ts_info, 0, sizeof (ts_info));
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface) >=
sizeof (req.ifr_name)) {
SCK_CloseSocket(sock_fd);
return -1;
}
ts_info.cmd = ETHTOOL_GET_TS_INFO;
req.ifr_data = (char *)&ts_info;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
SCK_CloseSocket(sock_fd);
return -1;
}
/* Simplify failure paths by closing the socket as early as possible */
SCK_CloseSocket(sock_fd);
sock_fd = -1;
if (ts_info.phc_index < 0) {
DEBUG_LOG("PHC missing on %s", req.ifr_name);
return -1;
}
if (snprintf(phc_device, sizeof (phc_device),
"/dev/ptp%d", ts_info.phc_index) >= sizeof (phc_device))
return -1;
return open(phc_device, flags);
#else
return -1;
#endif
}
/* ================================================== */
int
SYS_Linux_OpenPHC(const char *device, int flags)
{
int phc_fd = -1;
if (device[0] == '/') {
phc_fd = open(device, flags);
if (phc_fd >= 0)
phc_fd = verify_fd_is_phc(phc_fd);
}
if (phc_fd < 0) {
phc_fd = open_phc_by_iface_name(device, flags);
if (phc_fd < 0) {
LOG(LOGS_ERR, "Could not open PHC (of) %s", device);
return -1;
}
phc_fd = verify_fd_is_phc(phc_fd);
}
if (phc_fd >= 0)
UTI_FdSetCloexec(phc_fd);
return phc_fd;
}

View File

@@ -39,7 +39,7 @@ extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext conte
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_OpenPHC(const char *device, int flags);
extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec tss[][3]);

44
sys_linux_scmp.c Normal file
View File

@@ -0,0 +1,44 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2025
*
* 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.
*
**********************************************************************
=======================================================================
Lists of values that are needed in seccomp filters but need to
be compiled separately from sys_linux.c due to conflicting headers.
*/
#include <linux/termios.h>
#include "sys_linux_scmp.h"
unsigned long
SYS_Linux_GetExtraScmpIoctl(int index)
{
const unsigned long ioctls[] = {
#ifdef TCGETS2
/* Conflict between <linux/termios.h> and <sys/ioctl.h> */
TCGETS2,
#endif
0
};
return ioctls[index];
}

28
sys_linux_scmp.h Normal file
View File

@@ -0,0 +1,28 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2025
*
* 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 lists that are needed in seccomp filters but need to
be compiled separately from sys_linux.c due to conflicting headers.
*/
extern unsigned long SYS_Linux_GetExtraScmpIoctl(int index);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015, 2017
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015, 2017, 2025
*
* 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
@@ -257,6 +257,8 @@ SYS_Timex_Finalise(void)
int
SYS_Timex_Adjust(struct timex *txc, int ignore_error)
{
static long last_constant, last_freq;
static int last_status = -1;
int state;
#ifdef SOLARIS
@@ -270,7 +272,24 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
if (state < 0) {
LOG(ignore_error ? LOGS_DEBUG : LOGS_FATAL,
NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
return state;
}
/* This a good place to verify that nothing else is touching the clock,
without making an additional timex call. A clock update is normally
expected to have four driver calls:
- set_sync_status - primarily updating leap status
- set_frequency - correcting frequency error
- set_frequency - correcting phase error
- set_sync_status - updating leap and estimated/maximum error */
if (last_status != -1 &&
(((last_status ^ txc->status) & STA_PLL && !(txc->modes & MOD_STATUS)) ||
(last_constant != txc->constant && !(txc->modes & MOD_TIMECONST)) ||
(last_freq != txc->freq && !(txc->modes & MOD_FREQUENCY))))
LOG(LOGS_WARN, "System clock interference detected (another NTP client?)");
last_status = txc->status;
last_constant = txc->constant;
last_freq = txc->freq;
return state;
}

View File

@@ -10,7 +10,6 @@ for opts in \
"--enable-debug" \
"--enable-ntp-signd" \
"--enable-scfilter" \
"--disable-asyncdns" \
"--disable-ipv6" \
"--disable-privdrop" \
"--disable-readline" \
@@ -18,19 +17,13 @@ for opts in \
"--disable-sechash" \
"--disable-cmdmon" \
"--disable-cmdmon --enable-scfilter" \
"--disable-ntp" \
"--disable-ntp --enable-scfilter" \
"--disable-nts" \
"--disable-refclock" \
"--disable-timestamping" \
"--disable-timestamping --disable-ntp" \
"--disable-cmdmon --disable-ntp" \
"--disable-cmdmon --disable-ntp --enable-scfilter" \
"--disable-cmdmon --disable-refclock" \
"--disable-cmdmon --disable-ntp --disable-refclock"
"--disable-cmdmon --disable-refclock"
do
./configure $opts || exit 1
make clean
make "$@" || exit 1
make -C test/unit check || exit 1
make -C test/unit "$@" check || exit 1
done

View File

@@ -2,7 +2,7 @@
# Run the unit and simulation tests with different compiler sanitizers
# and under valgrind
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite"
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite --track-fds=yes"
cd ../..
@@ -25,6 +25,7 @@ touch Makefile
for extra_config_opts in \
"--all-privops" \
"--disable-ipv6" \
"--disable-cmdmon" \
"--disable-nts" \
"--disable-scfilter" \
"--without-aes-gcm-siv" \

View File

@@ -24,6 +24,12 @@ for falsetickers in 3 4; do
# These check are expected to fail
check_source_selection && test_fail
check_sync && test_fail
if [ $falsetickers = 3 ]; then
check_log_messages "Can't synchronise: no majority (only 2 of 5 sources agree)" 1 1 || test_fail
else
check_log_messages "Can't synchronise: no majority (no agreement among 5 sources)" 1 1 || test_fail
fi
done
# Sources with large asymmetric delay should be excluded
@@ -37,4 +43,35 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
# Sources with large distance should be ignored
servers=1
server_strata=2
server_conf="maxclockerror 1000"
jitter=1e-7
base_delay="(* -1.0 (equal 0.1 (min time 600) 600) (equal 0.1 from 2) (equal 0.1 to 1))"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_source_selection && test_fail
check_sync && test_fail
check_log_messages "Root distance of 192\.168\.123\.2 exceeds maxdistance of 3\." 1 1 || test_fail
# Sources with large jitter should be ignored
server_strata=1
server_conf=$default_server_conf
server_step="(pulse 64 64)"
base_delay=$default_base_delay
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_source_selection && test_fail
check_sync && test_fail
check_log_messages "Jitter of 192\.168\.123\.1 exceeds maxjitter of 1\." 1 1 || test_fail
test_pass

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env bash
. ./test.common
test_start "intermittent connection"
# Pass packets only for 1200 seconds every 10000 seconds
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1
(equal 0.1 (min (% time 10000) 1200) 1200)))
EOF
)
time_max_limit=1e-1
freq_max_limit=1e-2
time_rms_limit=2e-3
freq_rms_limit=2e-5
limit=100000
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
check_log_messages "Can't.*no selectable sources (1 unreachable" 9 10 || test_fail
check_log_messages "Selected source 192.168.123.1" 9 10 || test_fail
# Pass every 20th request
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1
(equal 0.1 from 2)
(equal 0.1 (min (% (sum 1) 20) 1) 1)))
EOF
)
time_max_limit=1e-2
freq_max_limit=1e-4
time_rms_limit=5e-3
max_sync_time=22000
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
check_log_messages "Can't.*no selectable sources (1 unreachable" 5 15 || test_fail
check_log_messages "Selected source 192.168.123.1" 5 15 || test_fail
test_pass

17
test/simulation/015-ipv6 Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
. ./test.common
test_start "IPv6 addressing"
check_config_h 'FEAT_IPV6 1' || test_skip
ip_family=6
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
test_pass

View File

@@ -52,7 +52,7 @@ client_conf="initstepslew 5 192.168.123.1 192.168.123.2"
min_sync_time=1
max_sync_time=500
server_conf="deny all"
server_conf="deny 192.168.0.0/16"
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -18,15 +18,30 @@ max_sync_time=70
chronyc_start=70
chronyc_conf="tracking"
for refclock in "SHM 0" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
for refclock in "SHM 0" "RTC /dev/rtc:utc" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
logdir tmp
log refclocks"
if [[ $refclock =~ RTC ]]; then
check_config_h 'FEAT_RTC 1' || continue
wander=0.0
freq_offset=0.0
client_chronyd_options="-x"
else
wander=$default_wander
freq_offset=$default_freq_offset
client_chronyd_options=$default_client_chronyd_options
fi
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if [[ $refclock =~ RTC ]]; then
check_chronyc_output "System time *: 0\.10000.... seconds fast" || test_fail
else
check_sync || test_fail
fi
check_chronyc_output "^Reference ID.*47505300 \(GPS\)
Stratum.*: 4
.*
@@ -35,11 +50,7 @@ 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 " 620 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.*[0-9] N " 997 1001 refclocks.log || test_fail
check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
done
@@ -90,8 +101,8 @@ Root delay : 0\.000000001 seconds
rm -f tmp/refclocks.log
min_sync_time=80
max_sync_time=180
chronyc_start=220
max_sync_time=260
chronyc_start=270
client_conf="
refclock SHM 0 refid NMEA offset 0.35 delay 0.1
refclock PPS /dev/pps0
@@ -112,8 +123,63 @@ Root delay : 0\.000000001 seconds
check_file_messages "20.* PPS1.*[0-9] N " 800 960 refclocks.log || test_fail
check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
min_sync_time=80
max_sync_time=180
chronyc_start=220
# Swapped order of SHM and PPS impacts first accepted sample
client_conf="
refclock PPS /dev/pps0
refclock SHM 0 refid NMEA offset 0.35 delay 0.1
logdir tmp
log refclocks
maxupdateskew 10000"
run_test || test_fail
check_chronyd_exit || test_fail
# This fails occasionally due to the 4th unreachable PPS update
# (made just before the SHM update) causing SHM unselection due to
# a large root distance
#check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^Reference ID.*50505330 \(PPS0\)
Stratum.*: 1
.*
Root delay : 0\.000000001 seconds
.*$" || test_fail
check_file_messages "20.* PPS0.*[0-9] N " 800 960 refclocks.log || test_fail
check_file_messages "20.* PPS0.*- N " 50 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
fi
export CLKNETSIM_PHC_JITTER_OFF=$[2 * 25 * 492]
export CLKNETSIM_PHC_JITTER_ON=$[2 * 25 * 8]
export CLKNETSIM_PHC_JITTER=1e-6
refclock_offset=0.0
refclock_jitter=1e-9
min_sync_time=5
max_sync_time=7
time_max_limit=1e-7
time_rms_limit=1e-8
client_conf="refclock PHC /dev/ptp0:nocrossts poll 0
logdir tmp
log refclocks"
chronyc_start=500
chronyc_conf="sources"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^MS.*
=*
#\* PHC0 0 0 377 8 .*$" || test_fail
rm -f tmp/refclocks.log
unset CLKNETSIM_PHC_JITTER_OFF
unset CLKNETSIM_PHC_JITTER_ON
export CLKNETSIM_PHC_JITTER=1e-7
refclock_offset="(+ 0.399 (sum 1e-3))"
refclock_jitter=1e-6
servers=1

View File

@@ -31,6 +31,17 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if check_config_h 'FEAT_IPV6 1'; then
ip_family=6
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
ip_family=$default_ip_family
fi
freq_max_limit=$default_freq_max_limit
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
client_peer_options=""

View File

@@ -114,7 +114,7 @@ limit=1
for chronyc_conf in \
"accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324 ipv6 ipv4" \
"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 maxunreach 8 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324 ipv6 ipv4" \
"add server node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \
@@ -145,7 +145,7 @@ for chronyc_conf in \
"dfreq 1.0e-3" \
"doffset -1.0" \
"dump" \
"local stratum 5 distance 1.0 activate 0.5 orphan" \
"local stratum 5 distance 1.0 activate 0.5 orphan waitsynced 100 waitunsynced 20" \
"local off" \
"makestep 10.0 3" \
"makestep" \
@@ -195,6 +195,38 @@ do
check_chronyc_output "501 Not authorised$" || test_fail
done
for chronyc_conf in \
"activity" \
"authdata" \
"clients" \
"manual list" \
"ntpdata" \
"rtcdata" \
"selectdata" \
"serverstats" \
"smoothing" \
"sourcename 192.168.123.1" \
"sources" \
"sourcestats" \
"tracking"
do
server_conf="opencommands ${chronyc_conf% *}"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "501 Not authorised$" && test_fail
server_conf="opencommands"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "501 Not authorised$" || test_fail
server_conf="cmddeny 192.168.123.2"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "506 Cannot talk to daemon$" || test_fail
done
server_conf="server 192.168.123.1"
cmdmon_unix=1
chronyc_conf="
@@ -242,14 +274,14 @@ Jitter asymmetry: \+0\.00
NTP tests : 111 111 1110
Interleaved : No
Authenticated : No
TX timestamping : Kernel
RX timestamping : Kernel
TX timestamping : (Daemon|Kernel)
RX timestamping : (Daemon|Kernel)
Total TX : 1
Total RX : 1
Total valid RX : 1
Total good RX : 0
Total kernel TX : [01]
Total kernel RX : 1
Total kernel RX : [01]
Total HW TX : 0
Total HW RX : 0
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
@@ -266,9 +298,9 @@ Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
NTP timestamp span : 0
NTP daemon RX timestamps : 0
NTP daemon RX timestamps : [01]
NTP daemon TX timestamps : 1
NTP kernel RX timestamps : 1
NTP kernel RX timestamps : [01]
NTP kernel TX timestamps : 0
NTP hardware RX timestamps : 0
NTP hardware TX timestamps : 0$" || test_fail
@@ -392,7 +424,7 @@ cyclelogs
dump
dfreq 1.0e-3
doffset -0.01
local stratum 5 distance 1.0 orphan
local stratum 5 distance 1.0 orphan waitsynced 100 waitunsynced 10
local off
makestep 10.0 3
makestep

View File

@@ -7,7 +7,7 @@ test_start "local options"
check_config_h 'FEAT_CMDMON 1' || test_skip
server_strata=3
server_conf="local stratum 5 orphan
server_conf="local stratum 5 orphan waitunsynced 0
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
@@ -23,11 +23,62 @@ check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
limit=1000
server_conf="local stratum 5 orphan
server 192.168.123.1 minpoll 6 maxpoll 6
server 192.168.123.2 minpoll 6 maxpoll 6
server 192.168.123.3 minpoll 6 maxpoll 6"
server_server_options="minpoll 6 maxpoll 6"
client_start=0
client_server_conf="
server 192.168.123.1 minpoll 6 maxpoll 6
server 192.168.123.2 minpoll 6 maxpoll 6
server 192.168.123.3 minpoll 6 maxpoll 6"
client_conf="logdir tmp
log measurements"
chronyc_start=700
chronyc_conf=""
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
check_file_messages "20.*123\.1.* 5 111 " 10 11 measurements.log || test_fail
check_file_messages "20.*123\.1.* [6-9] 111 " 0 0 measurements.log || test_fail
check_file_messages "20.*123\.2.* 5 111 " 2 4 measurements.log || test_fail
check_file_messages "20.*123\.2.* 6 111 " 7 9 measurements.log || test_fail
check_file_messages "20.*123\.2.* [7-9] 111 " 0 0 measurements.log || test_fail
check_file_messages "20.*123\.3.* 5 111 " 2 4 measurements.log || test_fail
check_file_messages "20.*123\.3.* 6 111 " 7 9 measurements.log || test_fail
check_file_messages "20.*123\.3.* [7-9] 111 " 0 0 measurements.log || test_fail
rm -f tmp/measurements.log
server_conf="local stratum 5 orphan distance 0.0 waitsynced 150 waitunsynced 0"
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1
(equal 0.1 from 1)
(equal 0.1 to 2)
(equal 0.1 (min time 500) 500)))
EOF
)
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
check_file_messages "20.*:1.:.*123\.1.* 5 111 " 6 6 measurements.log || test_fail
check_file_messages "20.*:0.:.*123\.2.* 5 111 " 2 3 measurements.log || test_fail
check_file_messages "20.*:1.:.*123\.2.* 5 111 " 6 7 measurements.log || test_fail
check_file_messages "20.*:0.:.*123\.3.* 5 111 " 7 10 measurements.log || test_fail
check_file_messages "20.*:1.:.*123\.3.* 5 111 " 0 1 measurements.log || test_fail
rm -f tmp/measurements.log
limit=4000
wander=0.0
jitter=0.0
server_strata=1
server_conf=""
server_server_options=""
client_server_conf=""
client_server_options="minpoll 6 maxpoll 6 minsamples 64"
chronyc_start=1
chronyc_conf="timeout 1000000

View File

@@ -22,6 +22,24 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 2.*$" || test_fail
check_chronyc_output "^.*Stratum *: 2
Ref time.*
System time *: 0.000.*
Last offset *: [+-]0.000.*$" || test_fail
check_log_messages "Could not step" 0 0 || test_fail
client_conf="makestep 0.01 -1"
client_server_options="offset 5.0005"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 2
Ref time.*
System time *: 5.000.*
Last offset *: [+-]0.000.*$" || test_fail
check_log_messages "Could not step" 20 60 || test_fail
test_pass

View File

@@ -58,6 +58,21 @@ for client_conf in \
fi
done
if check_config_h 'FEAT_IPV6 1'; then
server_chronyd_options="-6"
client_chronyd_options="-6"
ip_family=6
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
server_chronyd_options=$default_server_chronyd_options
client_chronyd_options="-d"
ip_family=$default_ip_family
fi
server_conf+="
server 192.168.123.2 minpoll 1 maxpoll 1 noselect"

View File

@@ -8,7 +8,9 @@ server_conf="broadcast 64 192.168.123.255"
client_server_options="offline"
run_test || test_fail
check_chronyd_exit || test_fail
if check_config_h 'FEAT_CMDMON 1'; then
check_chronyd_exit || test_fail
fi
check_packet_interval && test_fail
check_file_messages " 1 2 " 150 160 log.packets || test_fail

View File

@@ -56,6 +56,23 @@ check_file_messages " 2 1 .* 4460 " 260 300 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail
rm -f tmp/measurements.log
if check_config_h 'FEAT_IPV6 1'; then
ip_family=6
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages "20.*:123:1.* 111 111 1111" 75 80 measurements.log || test_fail
check_file_messages "20.*:123:1.* 111 001 0000" 37 39 measurements.log || test_fail
check_file_messages " 2 1 .* 4460 " 260 300 log.packets || test_fail
check_file_messages "." 6 6 ntskeys || test_fail
rm -f tmp/measurements.log
ip_family=$default_ip_family
fi
client_conf+="
ntsrefresh 120
ntsdumpdir tmp"

View File

@@ -4,6 +4,8 @@
test_start "address refreshment"
check_config_h 'FEAT_CMDMON 1' || test_skip
limit=1000
servers=5
client_conf="logdir tmp

View File

@@ -68,7 +68,7 @@ check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "Can't synchronise: no majority" 1 1 || test_fail
check_log_messages "Can't synchronise: no majority (no agreement among 2 sources)" 1 1 || test_fail
check_log_messages "Detected falseticker" 0 2 || test_fail
check_log_messages "Source 192.168.123.. replaced with" 3 60 || test_fail
check_log_messages "Source 192.168.123.1 replaced with" 1 25 || test_fail

189
test/simulation/149-sourcedir Executable file
View File

@@ -0,0 +1,189 @@
#!/usr/bin/env bash
. ./test.common
test_start "sourcedir directive"
check_config_h 'FEAT_CMDMON 1' || test_skip
servers=4
limit=191
update_executable="tmp/update-sourcedir"
client_server_conf="sourcedir tmp"
base_delay="(+ 1e-4 (* 5 (equal 0.1 from $[servers + 2])))"
chronyc_start=1
chronyc_conf="timeout 6000
activity
$(for i in $(seq 1 18); do echo "reload sources"; echo activity; done)"
cat > tmp/sources.sources <<EOF
pool nodes-1-2-3-4.net1.clk iburst
EOF
cat > tmp/update-sourcedir <<EOF
#!/usr/bin/env bash
case "\$1" in
19) s="pool nodes-1-2-3-4.net1.clk";;
39) s="pool nodes-1-2-3-4.net1.clk maxsources 3";;
59) s="pool nodes-1-2-3.net1.clk";;
79) s="pool nodes-1-2-3.net1.clk
server nodes-3-4.net1.clk";;
99) s="server nodes-3-4.net1.clk";;
119) s="server nodes-1-2-3.net1.clk";;
139) s="server 192.168.123.2";;
159) s="server 192.168.123.2 maxdelay 0.1";;
179) s="";;
*) exit 0;;
esac
echo "\$s" > tmp/sources.sources
EOF
chmod 755 tmp/update-sourcedir
run_test || test_fail
check_chronyd_exit || test_fail
check_log_messages "T00:0.:[135].Z \(Added\|Removed\)" 0 0 || test_fail
check_log_messages "T00:0.:..Z Added pool nodes-1-2-3-4\." 3 3 || test_fail
check_log_messages "T00:0.:..Z Removed pool nodes-1-2-3-4\." 3 3 || test_fail
check_log_messages "T00:0.:..Z Added pool nodes-1-2-3\." 1 1 || test_fail
check_log_messages "T00:0.:..Z Removed pool nodes-1-2-3\." 1 1 || test_fail
check_log_messages "T00:0.:..Z Added source ID#" 2 2 || test_fail
check_log_messages "T00:0.:..Z Added source 192.168.123.[1234]" 2 2 || test_fail
check_log_messages "T00:0.:..Z Removed source 192.168.123.[1234]" 4 4 || test_fail
check_chronyc_output "^200 OK
0 sources online
0 sources offline
4 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
0 sources with unknown address
200 OK
200 OK
4 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
4 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
4 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
3 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
3 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
3 sources online
0 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
5 sources with unknown address
200 OK
200 OK
3 sources online
0 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
5 sources with unknown address
200 OK
200 OK
4 sources online
0 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
5 sources with unknown address
200 OK
200 OK
4 sources online
0 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
5 sources with unknown address
200 OK
200 OK
1 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
1 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
1 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
1 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
1 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
1 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
1 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
1 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
0 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
0 sources with unknown address$" || test_fail
check_packet_interval || test_fail
test_pass

43
test/simulation/150-maxunreach Executable file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env bash
. ./test.common
test_start "maxunreach option"
limit=5000
servers=2
client_server_options="minpoll 6 maxpoll 6 minsamples 64"
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1
(equal 0.1 from 3)
(equal 0.1 to 1)
(equal 0.1 (min time 2000) 2000))
(* 0.5
(+ (equal 0.1 from 2)
(equal 0.1 to 2))))
EOF
)
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 "Selected source 192.168.123.1" 1 1 || test_fail
check_log_messages "Selected source 192.168.123.2" 0 0 || test_fail
client_server_options="minpoll 6 maxpoll 6 minsamples 64 maxunreach 10"
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 "Selected source 192.168.123.1" 1 1 || test_fail
check_log_messages "Selected source 192.168.123.2" 1 1 || test_fail
check_log_messages "00:52:..Z Selected source 192.168.123.2" 1 1 || test_fail
test_pass

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
. ./test.common
test_start "minstratum and maxstratum options"
client_conf="
minstratum 3
maxstratum 5"
for s in 3 5; do
server_conf="local stratum $s"
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 "Stratum of .* stratum" 0 0 || test_fail
done
for s in 2 6; do
server_conf="local stratum $s"
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 "Stratum of .* stratum" 0 0 || test_fail
if [ $s -lt 3 ]; then
check_log_messages "Stratum of .* below minstratum of 3" 1 1 || test_fail
else
check_log_messages "Stratum of .* above maxstratum of 5" 1 1 || test_fail
fi
done
test_pass

View File

@@ -1,7 +1,7 @@
This is a collection of simulation tests using the clknetsim simulator
(supported on Linux only).
https://github.com/mlichvar/clknetsim
https://gitlab.com/chrony/clknetsim
The CLKNETSIM_PATH environment variable should point to the directory where
clknetsim was downloaded and compiled. If the variable is not set, the tests

View File

@@ -39,6 +39,7 @@ default_refclock_jitter=""
default_refclock_offset=0.0
default_update_interval=0
default_update_executable=""
default_shift_pll=2
default_server_strata=1
@@ -78,6 +79,7 @@ default_max_sync_time=210
default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
default_ip_family=4
default_cmdmon_unix=1
default_pcap_dumps=0
default_dns=0
@@ -91,8 +93,6 @@ done
test_start() {
rm -rf tmp/*
echo "Testing $@:"
check_config_h 'FEAT_NTP 1' || test_skip
}
test_pass() {
@@ -192,6 +192,8 @@ get_node_name() {
if [ $dns -ne 0 ]; then
echo "node$index.net1.clk"
elif [ $ip_family -eq 6 ]; then
printf "fc00::123:%x" $index
else
echo "192.168.123.$index"
fi
@@ -239,7 +241,7 @@ check_config_h() {
# Check if the clock was well synchronized
check_sync() {
local i sync_time max_time_error max_freq_error ret=0
local i msg sync_time max_time_error max_freq_error r ret=0
local rms_time_error rms_freq_error
test_message 2 1 "checking clock sync time, max/rms time/freq error:"
@@ -254,17 +256,37 @@ check_sync() {
rms_time_error=$(get_stat 'RMS offset' $i)
rms_freq_error=$(get_stat 'RMS frequency' $i)
test_message 3 0 "node $i: $sync_time $(printf '%.2e %.2e %.2e %.2e' \
$max_time_error $max_freq_error $rms_time_error $rms_freq_error)"
r=0
msg="$sync_time"
if ! check_stat $sync_time $min_sync_time $max_sync_time; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $max_time_error)"
if ! check_stat $max_time_error 0.0 $time_max_limit; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $max_freq_error)"
if ! check_stat $max_freq_error 0.0 $freq_max_limit; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $rms_time_error)"
if ! check_stat $rms_time_error 0.0 $time_rms_limit; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $rms_freq_error)"
if ! check_stat $rms_freq_error 0.0 $freq_rms_limit; then
msg+="!"
r=1
fi
check_stat $sync_time $min_sync_time $max_sync_time && \
check_stat $max_time_error 0.0 $time_max_limit && \
check_stat $max_freq_error 0.0 $freq_max_limit && \
check_stat $rms_time_error 0.0 $time_rms_limit && \
check_stat $rms_freq_error 0.0 $freq_rms_limit && \
test_ok || test_bad
test_message 3 0 "node $i: $msg"
[ $? -eq 0 ] || ret=1
[ $r -eq 0 ] || ret=1
[ $r -eq 0 ] && test_ok || test_bad
done
return $ret
@@ -442,7 +464,9 @@ run_simulation() {
-o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \
-R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \
-r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \
-l $(awk "BEGIN {print $limit * 2^$update_interval}") && test_ok || test_error
-l $(awk "BEGIN {print $limit * 2^$update_interval}") \
$([ "$update_executable" != "" ] && printf "%s" "-e $update_executable") && \
test_ok || test_error
}
run_test() {
@@ -454,6 +478,7 @@ run_test() {
nodes=$(get_chronyd_nodes)
[ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients]
export CLKNETSIM_IP_FAMILY=$ip_family
export CLKNETSIM_UNIX_SUBNET=$[$cmdmon_unix != 0 ? 2 : 0]
for i in $(seq 1 $nodes); do

View File

@@ -4,7 +4,7 @@
check_chronyd_features PRIVDROP || test_skip "PRIVDROP support disabled"
user="nobody"
priv_drop=1
test_start "dropping of root privileges"

View File

@@ -68,7 +68,7 @@ check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atm
run_chronyc "clients" || test_fail
check_chronyc_output "^Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
127\.0\.0\.1 [0-9 ]+ 0 [-0-9 ]+ - [ 0-9]+ 0 0 - -$" \
.*127\.0\.0\.1 [0-9 ]+ 0 [-0-9 ]+ - [-0-9 ]+ 0 0 - -.*$" \
|| test_fail
run_chronyc "ntpdata $server" || test_fail
@@ -107,7 +107,7 @@ Total HW RX : 0$" || 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 -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
M 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns [\?N]$" || test_fail
run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+

View File

@@ -22,7 +22,7 @@ certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \
&> $TEST_DIR/certtool.log
certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \
--template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log
chown $user $TEST_DIR/server.*
chown "$(get_user)" $TEST_DIR/server.*
ntpport=$(get_free_port)
ntsport=$(get_free_port)

View File

@@ -26,7 +26,7 @@ certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \
&> $TEST_DIR/certtool.log
certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \
--template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log
chown $user $TEST_DIR/server.*
chown "$(get_user)" $TEST_DIR/server.*
ntpport=$(get_free_port)
ntsport=$(get_free_port)

View File

@@ -4,6 +4,8 @@
check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
[[ $CHRONYD_WRAPPER == *valgrind* ]] && test_skip "SCFILTER breaks valgrind"
test_start "system call filter in non-destructive tests"
for level in 1 2 -1 -2; do

View File

@@ -3,10 +3,11 @@
TEST_LIBDIR=${CHRONY_LIBDIR:-/var/lib/chrony}
TEST_LOGDIR=${CHRONY_LOGDIR:-/var/log/chrony}
TEST_RUNDIR=${CHRONY_RUNDIR:-/var/run/chrony}
TEST_PRIVDROP_USER=$(ls -ld "$TEST_RUNDIR" 2> /dev/null | awk '{print $3}')
. ./test.common
user=$(ls -ld "$TEST_RUNDIR" 2> /dev/null | awk '{print $3}')
priv_drop=1
test_start "system directories"

View File

@@ -4,6 +4,8 @@
check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
[[ $CHRONYD_WRAPPER == *valgrind* ]] && test_skip "SCFILTER breaks valgrind"
test_start "system call filter in destructive tests"
for level in 1 2 -1 -2; do

View File

@@ -21,9 +21,13 @@ TEST_LIBDIR=${TEST_LIBDIR:-$TEST_DIR}
TEST_LOGDIR=${TEST_LOGDIR:-$TEST_DIR}
TEST_RUNDIR=${TEST_RUNDIR:-$TEST_DIR}
TEST_SCFILTER=${TEST_SCFILTER:-0}
TEST_ROOT_USER=${TEST_ROOT_USER:-root}
TEST_PRIVDROP_USER=${TEST_PRIVDROP_USER:-nobody}
test_start() {
check_chronyd_features NTP CMDMON || test_skip "NTP/CMDMON support disabled"
local user=$(get_user)
check_chronyd_features CMDMON || test_skip "CMDMON support disabled"
[ "${#TEST_DIR}" -ge 5 ] || test_skip "invalid TEST_DIR"
@@ -36,7 +40,7 @@ test_start() {
rm -f "$TEST_LIBDIR"/* "$TEST_LOGDIR"/* "$TEST_RUNDIR"/*
if [ "$user" != "root" ]; then
if [ "$user" != "$TEST_ROOT_USER" ]; then
id -u "$user" > /dev/null 2> /dev/null || test_skip "missing user $user"
chown "$user:$(id -g "$user")" "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
su "$user" -s /bin/sh -c "touch $TEST_DIR/test" 2> /dev/null || \
@@ -108,7 +112,7 @@ default_clock_control=0
default_server=127.0.0.1
default_server_name=127.0.0.1
default_server_options=""
default_user=root
default_priv_drop=0
# Initialize test settings from their defaults
for defoptname in ${!default_*}; do
@@ -183,6 +187,14 @@ get_cmdsocket() {
echo "$TEST_RUNDIR/chronyd.sock"
}
get_user() {
if [ "$priv_drop" -ne 0 ]; then
echo "$TEST_PRIVDROP_USER"
else
echo "$TEST_ROOT_USER"
fi
}
# Find a free port in the 10000-20000 range (their use is racy)
get_free_port() {
local port
@@ -197,15 +209,19 @@ get_free_port() {
}
generate_chrony_conf() {
local ntpport cmdport
local user ntpport cmdport
user=$(get_user)
ntpport=$(get_free_port)
cmdport=$(get_free_port)
while true; do
cmdport=$(get_free_port)
[ "$ntpport" -ne "$cmdport" ] && break
done
echo "0.0 10000" > "$TEST_LIBDIR/driftfile"
echo "1 MD5 abcdefghijklmnopq" > "$TEST_DIR/keys"
chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR/keys"
echo "0.0" > "$TEST_DIR/tempcomp"
chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR"/{keys,tempcomp}
(
echo "pidfile $(get_pidfile)"
@@ -246,14 +262,14 @@ get_chronyd_options() {
[ "$clock_control" -eq 0 ] && echo "-x"
echo "-l $(get_logfile)"
echo "-f $(get_conffile)"
echo "-u $user"
echo "-u $(get_user)"
echo "-F $TEST_SCFILTER"
echo "$extra_chronyd_options"
}
# Start a chronyd instance
start_chronyd() {
local pid pidfile=$(get_pidfile)
local pid pidfile=$(get_pidfile) wrapper_options=""
print_nondefaults
test_message 1 0 "starting chronyd"
@@ -264,7 +280,12 @@ start_chronyd() {
rm -f "$TEST_LOGDIR"/*.log
$CHRONYD_WRAPPER "$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1
if [[ $CHRONYD_WRAPPER == *valgrind* ]]; then
wrapper_options="--log-file=$TEST_DIR/chronyd.valgrind --enable-debuginfod=no"
fi
$CHRONYD_WRAPPER $wrapper_options \
"$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1
[ $? -eq 0 ] && [ -f "$pidfile" ] && ps -p "$(cat "$pidfile")" > /dev/null && test_ok || test_error
}
@@ -311,6 +332,12 @@ stop_chronyd() {
done
test_ok
if [ -f "$TEST_DIR/chronyd.valgrind" ]; then
test_message 2 0 "checking valgrind report"
! grep -q 'ERROR SUMMARY: [^0]' "$TEST_DIR/chronyd.valgrind" && \
test_ok || test_bad
fi
}
# Check chronyd log for expected and unexpected messages
@@ -351,7 +378,7 @@ check_chronyd_files() {
# Run a chronyc command
run_chronyc() {
local host=$chronyc_host options="-n -m"
local host=$chronyc_host options="-n -m" wrapper_options="" ret=0
test_message 1 0 "running chronyc $([ -n "$host" ] && echo "@$host ")$*"
@@ -361,8 +388,23 @@ run_chronyc() {
options="$options -p $(grep cmdport "$(get_conffile)" | awk '{print $2}')"
fi
$CHRONYC_WRAPPER "$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \
test_ok || test_error
if [[ $CHRONYC_WRAPPER == *valgrind* ]]; then
wrapper_options="--log-file=$TEST_DIR/chronyc.valgrind --enable-debuginfod=no"
fi
$CHRONYC_WRAPPER $wrapper_options \
"$chronyc" -h "$host" -u "$(get_user)" $options "$@" > "$TEST_DIR/chronyc.out" && \
test_ok || test_error
[ $? -ne 0 ] && ret=1
if [ -f "$TEST_DIR/chronyc.valgrind" ]; then
test_message 2 0 "checking valgrind report"
! grep -q 'ERROR SUMMARY: [^0]' "$TEST_DIR/chronyc.valgrind" && \
test_ok || test_bad
[ $? -ne 0 ] && ret=1
fi
return $ret
}
# Compare chronyc output with specified pattern

View File

@@ -21,8 +21,6 @@
#include <config.h>
#include "test.h"
#if defined(FEAT_NTP) || defined(FEAT_CMDMON)
#include <clientlog.c>
static uint64_t
@@ -321,10 +319,3 @@ test_unit(void)
LCL_Finalise();
CNF_Finalise();
}
#else
void
test_unit(void)
{
TEST_REQUIRE(0);
}
#endif

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