Compare commits

...

111 Commits
4.6 ... 4.7

Author SHA1 Message Date
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
Miroslav Lichvar
b0ac5992fb doc: update NEWS 2024-10-08 14:49:43 +02:00
Miroslav Lichvar
cd65e32cf0 doc: warn about MD5 keys not protecting extension fields
Add a warning to the chrony.conf man page that MD5 keys cannot protect
NTP extension fields due to the length extension attack.
2024-10-08 14:49:41 +02:00
Miroslav Lichvar
b9f5278846 update copyright years 2024-10-08 12:11:32 +02:00
Miroslav Lichvar
b8b166044f nts: don't include compliant-128gcm record for other AEADs
If the client included the NTS-KE record requesting compliant key
exporter context for AES-128-GCM-SIV, but the server doesn't select this
AEAD algorithm (it's not supported by the crypto library or it is
disabled by the ntsaeads directive), don't include the NTS-KE record in
the response. It's not relevant to the other AEAD algorithms.
2024-10-08 12:11:05 +02:00
Miroslav Lichvar
42fbf41686 nts: make server and client AEAD algorithms configurable
Add ntsaeads directive to specify a list of AEAD algorithms enabled for
NTS. The list is shared between the server and client. For the client it
also specifies the order of priority. The default is "30 15", matching
the previously hardcoded preference of AES-128-GCM-SIV (30) over
AES-SIV-CMAC-256 (15).
2024-10-03 16:09:53 +02:00
Miroslav Lichvar
79a790e6b5 test: improve nts_ke_client unit test 2024-10-03 15:02:16 +02:00
Miroslav Lichvar
f5cd79d2df nts: check TLS session in NKSN_GetKeys()
Make sure the TLS session is not NULL in NKSN_GetKeys() before trying to
export the keys in case some future code tried to call the function
outside of the NTS-KE message handler.
2024-10-03 15:02:16 +02:00
Miroslav Lichvar
689605b6a2 nts: switch client to compliant key exporter on NTS NAK
Implement a fallback for the NTS-NTP client to switch to the compliant
AES-128-GCM-SIV exporter context when the server is using the compliant
context, but does not support the new NTS-KE record negotiating its use,
assuming it can respond with an NTS NAK to the request authenticated
with the incorrect key.

Export both sets of keys when processing the NTS-KE response. If an NTS
NAK is the only valid response from the server after the last NTS-KE
session, switch to the keys exported with the compliant context for the
following requests instead of dropping all cookies and restarting
NTS-KE. Don't switch back to the original keys if an NTS NAK is received
again.
2024-10-03 15:02:03 +02:00
Miroslav Lichvar
0707865413 nts: negotiate compliant export of AES-128-GCM-SIV keys
Add client and server support for a new NTS-KE record to negotiate use
of the compliant key exporter context with the AES-128-GCM-SIV AEAD as
specified here:

https://chrony-project.org/doc/spec/nts-compliant-128gcm.html
2024-09-26 16:04:01 +02:00
Miroslav Lichvar
2adda9c12c nts: construct key exporter context
When the NTS client and server negotiated use of AES-128-GCM-SIV keys,
the keys exported from the TLS session and used for authentication and
encryption of NTP messages do not comply to RFC8915. The exporter
context value specified in the section 5.1 of RFC8915 function is
incorrect. It is a hardcoded string which contains 15 (AES-SIV-CMAC-256)
instead of 30 (AES-128-GCM-SIV). This causes chrony to not interoperate
with NTS implementations that follow RFC8915 correctly. (At this time,
there doesn't seem to be another implementation with AES-128-GCM-SIV
support yet.)

Replace the string with a proper construction of the exporter context
from a specified AEAD ID and next protocol.

Keep using the incorrect AEAD ID for AES-128-GCM-SIV to not break
compatibility with existing chrony servers and clients. A new NTS-KE
record will be added to negotiate the compliant exporter context.

Reported-by: Martin Mayer <martin.mayer@m2-it-solutions.de>
2024-09-26 12:45:44 +02:00
108 changed files with 3167 additions and 2064 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)

44
NEWS
View File

@@ -1,3 +1,47 @@
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
====================
Enhancements
------------
* Add ntsaeads directive to enable only selected AEAD algorithms for NTS
Workarounds
-----------
* Negotiate use of compliant NTS keys with AES-128-GCM-SIV AEAD algorithm
(by default the keys are generated differently than in RFC 8915 for
compatibility with chrony server and client versions 4.4, 4.5, and 4.6)
* Switch to compliant NTS keys if first response from server is NTS NAK
New in version 4.6
==================

4
README
View File

@@ -84,11 +84,14 @@ 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 +124,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>

17
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;
@@ -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 */

View File

@@ -125,12 +125,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);
@@ -754,12 +753,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 +772,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 +907,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 +930,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 +946,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);
@@ -993,6 +991,15 @@ process_cmd_add_source(CMD_Request *msg, char *line)
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;
}
@@ -3440,7 +3447,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",
@@ -3588,6 +3595,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;

668
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();
}
@@ -1439,18 +1357,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 +1605,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 +1701,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

@@ -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;
@@ -82,7 +87,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 +109,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 +128,78 @@ 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, "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 +300,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 +311,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);

343
conf.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2017, 2020
* 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,13 +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 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 *);
@@ -66,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 *);
@@ -78,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);
@@ -97,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;
@@ -131,6 +138,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 */
@@ -261,7 +270,10 @@ static char *user;
/* Address refresh interval */
static int refresh = 1209600; /* 2 weeks */
#define DEFAULT_NTS_AEADS "30 15"
/* NTS server and client configuration */
static ARR_Instance nts_aeads; /* array of int */
static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL;
static ARR_Instance nts_server_cert_files; /* array of (char *) */
@@ -320,6 +332,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;
@@ -349,12 +366,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 : "");
}
/* ================================================== */
@@ -397,6 +422,8 @@ check_number_of_args(char *line, int num)
void
CNF_Initialise(int r, int client_only)
{
char buf[128];
restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
@@ -410,6 +437,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, 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 *));
@@ -466,9 +500,12 @@ CNF_Finalise(void)
ARR_DestroyInstance(refclock_sources);
ARR_DestroyInstance(broadcasts);
ARR_DestroyInstance(open_commands);
ARR_DestroyInstance(ntp_restrictions);
ARR_DestroyInstance(cmd_restrictions);
ARR_DestroyInstance(nts_aeads);
ARR_DestroyInstance(nts_server_cert_files);
ARR_DestroyInstance(nts_server_key_files);
ARR_DestroyInstance(nts_trusted_certs_paths);
@@ -563,7 +600,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")) {
@@ -591,7 +628,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);
@@ -604,9 +641,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")) {
@@ -634,11 +671,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")) {
@@ -648,7 +685,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")) {
@@ -660,25 +697,27 @@ 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, "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, "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, 0, INT_MAX);
} else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
@@ -688,19 +727,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")) {
@@ -708,18 +749,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")) {
@@ -729,11 +770,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")) {
@@ -752,7 +793,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;
@@ -760,48 +801,70 @@ 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 void
parse_ints(char *line, ARR_Instance array, int min, int max)
{
char *s;
int v;
ARR_SetSize(array, 0);
while (*line) {
s = line;
line = CPS_SplitWord(line);
parse_int(s, &v, min, max);
ARR_AppendElement(array, &v);
}
}
/* ================================================== */
@@ -809,6 +872,7 @@ parse_null(char *line)
static void
parse_source(char *line, char *type, int fatal)
{
CPS_Status status;
NTP_Source source;
if (strcasecmp(type, "peer") == 0) {
@@ -829,9 +893,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;
}
@@ -866,7 +934,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;
}
@@ -945,31 +1013,28 @@ 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, "offset")) {
if (sscanf(line, "%lf%n", &offset, &n) != 1)
@@ -987,8 +1052,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;
@@ -1000,13 +1064,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;
}
@@ -1076,8 +1140,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;
}
@@ -1238,6 +1310,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)
{
@@ -1479,17 +1617,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)
@@ -1624,6 +1762,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)
{
@@ -1711,8 +1872,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)
@@ -1751,13 +1912,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,
@@ -1949,8 +2113,9 @@ CNF_GetAcquisitionPort(void)
/* ================================================== */
char *
CNF_GetDriftFile(void)
CNF_GetDriftFile(int *interval)
{
*interval = drift_file_interval;
return drift_file;
}
@@ -2173,6 +2338,14 @@ CNF_GetManualEnabled(void)
/* ================================================== */
ARR_Instance
CNF_GetOpenCommands(void)
{
return open_commands;
}
/* ================================================== */
int
CNF_GetCommandPort(void) {
return cmd_port;
@@ -2181,13 +2354,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;
@@ -2599,6 +2775,14 @@ CNF_GetRefresh(void)
/* ================================================== */
ARR_Instance
CNF_GetNtsAeads(void)
{
return nts_aeads;
}
/* ================================================== */
char *
CNF_GetNtsDumpDir(void)
{
@@ -2675,8 +2859,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

@@ -29,6 +29,7 @@
#define GOT_CONF_H
#include "addressing.h"
#include "array.h"
#include "reference.h"
#include "sources.h"
@@ -55,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);
@@ -69,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);
@@ -108,7 +110,8 @@ 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);
@@ -163,6 +166,7 @@ extern int CNF_GetPtpDomain(void);
extern int CNF_GetRefresh(void);
extern ARR_Instance CNF_GetNtsAeads(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);

83
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
@@ -219,7 +217,6 @@ EXTRA_CLI_OBJECTS=""
feat_debug=0
feat_cmdmon=1
feat_ntp=1
feat_refclock=1
feat_readline=1
try_editline=1
@@ -243,7 +240,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,7 +249,6 @@ feat_timestamping=1
try_timestamping=0
feat_ntp_signd=0
ntp_era_split=""
use_pthread=0
default_user="root"
default_hwclockfile=""
default_pidfile="/var/run/chrony/chronyd.pid"
@@ -308,9 +303,6 @@ do
--disable-cmdmon)
feat_cmdmon=0
;;
--disable-ntp)
feat_ntp=0
;;
--disable-refclock)
feat_refclock=0
;;
@@ -341,9 +333,6 @@ do
--without-seccomp)
try_seccomp=0
;;
--disable-asyncdns)
feat_asyncdns=0
;;
--disable-forcednsretry)
feat_forcednsretry=0
;;
@@ -504,27 +493,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"
@@ -705,15 +682,15 @@ 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" ] && \
@@ -816,12 +793,10 @@ 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"
fi
@@ -860,7 +835,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 +888,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 +898,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 +918,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 +960,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 +980,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,13 +1007,6 @@ 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
@@ -1045,10 +1020,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
@@ -1114,7 +1085,7 @@ 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"

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,19 @@ 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
response to the 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.
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 +116,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 +136,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 +153,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 +166,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 +206,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
@@ -244,11 +252,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 +300,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,7 +311,7 @@ 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
known to be unreliable or inaccurate and that should be used only when other
sources are unreachable.
*version* _version_:::
This option sets the NTP version of packets sent to the server. This can be
@@ -311,7 +319,7 @@ 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 +382,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 +406,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 +434,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 +463,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 +486,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 +516,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 +565,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 +605,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 +648,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 +673,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 +687,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*:::
@@ -833,6 +858,34 @@ changes in the frequency and offset of the clock. The offsets in the
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
_statistics.log_ files) may be smaller than the actual offsets.
[[ntsaeads1]]*ntsaeads* _ID_...::
This directive specifies a list of IDs of Authenticated Encryption with
Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
messages. The algorithms are specified in decreasing order of priority.
Algorithms that are not supported by the installed version of the crypto
library (Nettle, GnuTLS) are ignored.
+
The following IDs are supported:
+
* 15: AES-SIV-CMAC-256
* 30: AES-128-GCM-SIV
{blank}::
+
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.
+
The ID of the used algorithm is reported for each server by the
<<chronyc.adoc#authdata,*authdata*>> command.
+
An example of the directive is:
+
----
ntsaeads 15
----
+
This list is used also by the <<ntsaeads2,NTS server>>.
[[ntsdumpdir1]]*ntsdumpdir* _directory_::
This directive specifies a directory for the client to save NTS cookies it
received from the server in order to avoid making an NTS-KE request when
@@ -861,7 +914,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.
+
@@ -896,7 +949,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
@@ -910,17 +963,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.
@@ -933,11 +987,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
@@ -1013,7 +1067,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_::
@@ -1042,14 +1096,14 @@ 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.
@@ -1069,11 +1123,11 @@ distances are in milliseconds.
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
default, the precision is measured on start-up as the minimum time to read the
clock.
+
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
The measured value works well in most cases. It generally overestimates the
precision and it can be sensitive to the CPU speed, however, 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'
@@ -1096,7 +1150,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
@@ -1109,7 +1163,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.
+
@@ -1128,6 +1182,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:
+
----
@@ -1151,7 +1209,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`'.
@@ -1210,7 +1268,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
@@ -1243,15 +1301,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.
@@ -1293,7 +1351,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
@@ -1315,6 +1373,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
@@ -1522,13 +1583,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
@@ -1555,7 +1616,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.
@@ -1573,7 +1634,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_::
@@ -1667,16 +1728,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:
@@ -1699,7 +1763,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
@@ -1709,12 +1773,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_::
@@ -1763,7 +1845,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_::
@@ -1771,7 +1853,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
@@ -1779,6 +1861,43 @@ per process that the NTS server will accept. The default value is 100. The
maximum practical value is half of the system *FD_SETSIZE* constant (usually
1024).
[[ntsaeads2]]*ntsaeads* _ID_...::
This directive specifies a list of IDs of Authenticated Encryption with
Associated Data (AEAD) algorithms enabled for NTS authentication of NTP
messages. *chronyd* as a server uses the first enabled algorithm from the list
provided by the client. Algorithms that are not supported by the installed
version of the crypto library (Nettle, GnuTLS) are ignored.
+
The following IDs are supported:
+
* 15: AES-SIV-CMAC-256
* 30: AES-128-GCM-SIV
{blank}::
+
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.
+
An example of the directive is:
+
----
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 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
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
NAK.
[[ntsdumpdir2]]*ntsdumpdir* _directory_::
This directive specifies a directory where *chronyd* operating as an NTS server
can save the keys which encrypt NTS cookies provided to clients. The keys are
@@ -1796,9 +1915,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.
@@ -1850,7 +1969,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.
+
@@ -1868,7 +1987,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
@@ -1876,7 +1995,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
@@ -1884,7 +2003,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}::
+
@@ -1913,13 +2032,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
@@ -1933,7 +2052,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.
@@ -2017,6 +2136,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.
@@ -2058,6 +2180,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_::
@@ -2123,7 +2266,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
@@ -2138,7 +2281,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
@@ -2211,7 +2355,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.
+
@@ -2252,7 +2396,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.
@@ -2471,9 +2615,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
@@ -2563,7 +2707,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.
@@ -2649,8 +2793,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:
@@ -2690,7 +2834,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.
@@ -2705,7 +2849,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.
+
@@ -2732,7 +2876,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
@@ -2756,7 +2900,11 @@ source is specified in the configuration file with a key shorter than 80 bits.
+
The recommended key types are AES ciphers and SHA3 hash functions. MD5 should
be avoided unless no other type is supported on the server and client, or
peers.
peers. A major weakness of MD5 for the NTP MAC is a length extension attack,
where a man-in-the-middle attacker can add arbitrary extension fields to the
NTP message and update the MAC to pass the verification of the extended
message. The *extfield* option (enabling processing of the specified extension
field) should not be used for NTP sources authenticated with an MD5 key.
+
The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
generate random keys for the key file. By default, it generates 160-bit MD5 or
@@ -2787,7 +2935,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
@@ -2800,7 +2948,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.
@@ -2857,7 +3005,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
@@ -2883,7 +3031,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
@@ -2972,7 +3120,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
@@ -2987,7 +3135,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.
@@ -3055,7 +3203,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
@@ -3066,7 +3214,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.
@@ -3109,7 +3257,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-2023
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-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
@@ -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.
@@ -364,9 +367,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
@@ -1461,6 +1467,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

@@ -338,10 +338,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

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);
@@ -199,8 +201,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);

2
keys.c
View File

@@ -405,7 +405,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);
}
/* ================================================== */

19
local.c
View File

@@ -184,10 +184,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 +223,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 +297,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 +695,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 */
@@ -690,6 +691,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
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 +1744,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 +1960,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 +2024,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 +2337,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);
if (phc_fd < 0)
return 0;

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

@@ -40,6 +40,7 @@
#define NKE_RECORD_COOKIE 5
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
#define NKE_RECORD_COMPLIANT_128GCM_EXPORT 1024
#define NKE_NEXT_PROTOCOL_NTPV4 0
@@ -49,8 +50,6 @@
#define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0"
#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1"
#define NKE_MAX_MESSAGE_LENGTH 16384
#define NKE_MAX_RECORD_BODY_LENGTH 256

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020-2021
* Copyright (C) Miroslav Lichvar 2020-2021, 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
@@ -50,7 +50,9 @@ struct NKC_Instance_Record {
int got_response;
int resolving_name;
int compliant_128gcm;
NKE_Context context;
NKE_Context alt_context;
NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
@@ -98,12 +100,14 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
/* ================================================== */
#define MAX_AEAD_ALGORITHMS 4
static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
uint16_t data[2];
int length;
uint16_t data[MAX_AEAD_ALGORITHMS];
int i, aead_algorithm, length;
NKSN_BeginMessage(session);
@@ -111,15 +115,24 @@ prepare_request(NKC_Instance inst)
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0;
length = 0;
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
data[length++] = htons(AEAD_AES_128_GCM_SIV);
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
data[length++] = htons(AEAD_AES_SIV_CMAC_256);
for (i = length = 0; i < ARR_GetSize(CNF_GetNtsAeads()) && length < MAX_AEAD_ALGORITHMS;
i++) {
aead_algorithm = *(int *)ARR_GetElement(CNF_GetNtsAeads(), i);
if (SIV_GetKeyLength(aead_algorithm) > 0)
data[length++] = htons(aead_algorithm);
}
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
length * sizeof (data[0])))
return 0;
for (i = 0; i < length; i++) {
if (data[i] == htons(AEAD_AES_128_GCM_SIV)) {
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
return 0;
break;
}
}
if (!NKSN_EndMessage(session))
return 0;
@@ -139,6 +152,8 @@ process_response(NKC_Instance inst)
assert(sizeof (data) % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2);
inst->compliant_128gcm = 0;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
inst->num_cookies = 0;
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
inst->ntp_address.port = 0;
@@ -165,15 +180,31 @@ process_response(NKC_Instance inst)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
if (length != 2) {
DEBUG_LOG("Unexpected AEAD algorithm");
error = 1;
break;
}
aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm;
for (i = 0; i < ARR_GetSize(CNF_GetNtsAeads()); i++) {
if (ntohs(data[0]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), i) &&
SIV_GetKeyLength(ntohs(data[0])) > 0) {
aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm;
}
}
if (aead_algorithm < 0) {
DEBUG_LOG("Unexpected AEAD algorithm");
error = 1;
}
break;
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
if (length != 0) {
DEBUG_LOG("Non-empty compliant-128gcm record");
error = 1;
break;
}
DEBUG_LOG("Compliant AES-128-GCM-SIV export");
inst->compliant_128gcm = 1;
break;
case NKE_RECORD_ERROR:
if (length == 2)
@@ -255,6 +286,7 @@ process_response(NKC_Instance inst)
static int
handle_message(void *arg)
{
SIV_Algorithm exporter_algorithm;
NKC_Instance inst = arg;
if (!process_response(inst)) {
@@ -262,8 +294,25 @@ handle_message(void *arg)
return 0;
}
if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
&inst->context.c2s, &inst->context.s2c))
exporter_algorithm = inst->context.algorithm;
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
context incorrectly for compatibility with older chrony servers unless
the server confirmed support for the compliant context. Generate both
sets of keys in case the server uses the compliant context, but does not
support the negotiation record, assuming it will respond with an NTS NAK
to a request authenticated with the noncompliant key. */
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm) {
inst->alt_context.algorithm = inst->context.algorithm;
if (!NKSN_GetKeys(inst->session, inst->alt_context.algorithm, exporter_algorithm,
NKE_NEXT_PROTOCOL_NTPV4, &inst->alt_context.c2s, &inst->alt_context.s2c))
return 0;
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
}
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
return 0;
if (inst->server_name[0] != '\0') {
@@ -428,7 +477,7 @@ NKC_IsActive(NKC_Instance inst)
/* ================================================== */
int
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
@@ -438,6 +487,7 @@ NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
return 0;
*context = inst->context;
*alt_context = inst->alt_context;
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
cookies[i] = inst->cookies[i];

View File

@@ -46,7 +46,7 @@ extern int NKC_Start(NKC_Instance inst);
extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020, 2022
* Copyright (C) Miroslav Lichvar 2020, 2022, 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
@@ -337,8 +337,10 @@ helper_signal(int x)
/* ================================================== */
static int
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm,
int compliant_128gcm)
{
SIV_Algorithm exporter_algorithm;
NKE_Context context;
NKE_Cookie cookie;
char *ntp_server;
@@ -371,6 +373,11 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
return 0;
if (aead_algorithm == AEAD_AES_128_GCM_SIV && compliant_128gcm) {
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
return 0;
}
if (CNF_GetNTPPort() != NTP_PORT) {
datum = htons(CNF_GetNTPPort());
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
@@ -385,8 +392,16 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
}
context.algorithm = aead_algorithm;
exporter_algorithm = aead_algorithm;
if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c))
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
context incorrectly for compatibility with older chrony clients unless
the client requested the compliant context */
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !compliant_128gcm)
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
if (!NKSN_GetKeys(session, aead_algorithm, exporter_algorithm,
NKE_NEXT_PROTOCOL_NTPV4, &context.c2s, &context.s2c))
return 0;
for (i = 0; i < NKE_MAX_COOKIES; i++) {
@@ -411,7 +426,8 @@ process_request(NKSN_Instance session)
int next_protocol_records = 0, aead_algorithm_records = 0;
int next_protocol_values = 0, aead_algorithm_values = 0;
int next_protocol = -1, aead_algorithm = -1, error = -1;
int i, critical, type, length;
int i, j, critical, type, length;
int compliant_128gcm = 0;
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
@@ -446,11 +462,21 @@ process_request(NKSN_Instance session)
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++;
/* Use the first supported algorithm */
if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
aead_algorithm = ntohs(data[i]);
/* Use the first enabled and supported algorithm */
for (j = 0; j < ARR_GetSize(CNF_GetNtsAeads()); j++) {
if (ntohs(data[i]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), j) &&
aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
aead_algorithm = ntohs(data[i]);
}
}
break;
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
if (length != 0) {
error = NKE_ERROR_BAD_REQUEST;
break;
}
compliant_128gcm = 1;
break;
case NKE_RECORD_ERROR:
case NKE_RECORD_WARNING:
case NKE_RECORD_COOKIE:
@@ -469,7 +495,7 @@ process_request(NKSN_Instance session)
error = NKE_ERROR_BAD_REQUEST;
}
if (!prepare_response(session, error, next_protocol, aead_algorithm))
if (!prepare_response(session, error, next_protocol, aead_algorithm, compliant_128gcm))
return 0;
return 1;
@@ -494,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
@@ -508,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 */
@@ -676,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;
@@ -703,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();
@@ -766,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);
@@ -931,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

@@ -663,8 +663,7 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
goto error;
if (certs && keys) {
if (trusted_certs || trusted_certs_ids)
assert(0);
BRIEF_ASSERT(!trusted_certs && !trusted_certs_ids);
for (i = 0; i < n_certs_keys; i++) {
if (!UTI_CheckFilePermissions(keys[i], 0771))
@@ -675,8 +674,7 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
goto error;
}
} else {
if (certs || keys || n_certs_keys > 0)
assert(0);
BRIEF_ASSERT(!certs && !keys && n_certs_keys <= 0);
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
@@ -877,22 +875,42 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
/* ================================================== */
int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{
int length = SIV_GetKeyLength(siv);
int length = SIV_GetKeyLength(algorithm);
struct {
uint16_t next_protocol;
uint16_t algorithm;
uint8_t is_s2c;
uint8_t _pad;
} context;
if (!inst->tls_session)
return 0;
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
DEBUG_LOG("Invalid algorithm");
return 0;
}
assert(sizeof (context) == 6);
context.next_protocol = htons(next_protocol);
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 (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
length, (char *)c2s->key) < 0 ||
gnutls_prf_rfc5705(inst->tls_session,
sizeof (context) - 1, (char *)&context,
length, (char *)c2s->key) < 0) {
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 (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
sizeof (context) - 1, (char *)&context,
length, (char *)s2c->key) < 0) {
DEBUG_LOG("Could not export key");
return 0;

View File

@@ -77,8 +77,11 @@ extern int NKSN_EndMessage(NKSN_Instance inst);
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length);
/* Export NTS keys for a specified algorithm */
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
/* Export NTS keys for a specified algorithm (for compatibility reasons the
RFC5705 exporter context is allowed to have a different algorithm) */
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm,
SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c);
/* Check if the session has stopped */
extern int NKSN_IsStopped(NKSN_Instance inst);

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

@@ -72,6 +72,7 @@ struct NNC_Instance_Record {
double last_nke_success;
NKE_Context context;
NKE_Context alt_context;
unsigned int context_id;
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
@@ -105,6 +106,7 @@ reset_instance(NNC_Instance inst)
inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context));
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
inst->context_id = 0;
memset(inst->cookies, 0, sizeof (inst->cookies));
inst->num_cookies = 0;
@@ -165,6 +167,21 @@ check_cookies(NNC_Instance inst)
if (inst->num_cookies > 0 &&
((inst->nak_response && !inst->ok_response) ||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
/* Before dropping the cookies, check whether there is an alternate set of
keys available (exported with the compliant context for AES-128-GCM-SIV)
and the NAK was the only valid response after the last NTS-KE session,
indicating we use incorrect keys and switching to the other set of keys
for the following NTP requests might work */
if (inst->alt_context.algorithm != AEAD_SIV_INVALID &&
inst->alt_context.algorithm == inst->context.algorithm &&
inst->nke_attempts > 0 && inst->nak_response && !inst->ok_response) {
inst->context = inst->alt_context;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
DEBUG_LOG("Switched to compliant keys");
return 1;
}
inst->num_cookies = 0;
DEBUG_LOG("Dropped cookies");
}
@@ -261,7 +278,7 @@ get_cookies(NNC_Instance inst)
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
/* Get the new keys, cookies and NTP address if the session was successful */
got_data = NKC_GetNtsData(inst->nke, &inst->context,
got_data = NKC_GetNtsData(inst->nke, &inst->context, &inst->alt_context,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address);
@@ -520,6 +537,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
new NTS-KE session to be started as soon as the cookies run out. */
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
return 1;
}
@@ -643,6 +661,7 @@ load_cookies(NNC_Instance inst)
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
goto error;
inst->alt_context.algorithm = AEAD_SIV_INVALID;
inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
sizeof (inst->context.s2c.key));
@@ -687,6 +706,7 @@ error:
fclose(f);
memset(&inst->context, 0, sizeof (inst->context));
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
inst->num_cookies = 0;
}

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,29 +231,16 @@ 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,
@@ -665,6 +657,12 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 1;
}
void
RCL_UpdateReachability(RCL_Instance instance)
{
instance->reached++;
}
double
RCL_GetPrecision(RCL_Instance instance)
{
@@ -792,6 +790,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 +808,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 +816,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

@@ -81,6 +81,7 @@ extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);
extern void RCL_UpdateReachability(RCL_Instance instance);
extern double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance);

View File

@@ -74,7 +74,7 @@ static int phc_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance);
phc_fd = SYS_Linux_OpenPHC(path, 0);
phc_fd = SYS_Linux_OpenPHC(path);
if (phc_fd < 0)
LOG_FATAL("Could not open PHC");
@@ -154,6 +154,8 @@ static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
}
phc->last_extts = *phc_ts;
RCL_UpdateReachability(instance);
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
return;
@@ -204,6 +206,9 @@ static int phc_poll(RCL_Instance instance)
if (n_readings < 1)
return 0;
if (!phc->extpps)
RCL_UpdateReachability(instance);
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
return 0;

View File

@@ -143,6 +143,8 @@ static int pps_poll(RCL_Instance instance)
pps->last_seq = seq;
RCL_UpdateReachability(instance);
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
}

182
refclock_rtc.c Normal file
View File

@@ -0,0 +1,182 @@
/*
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;
RCL_UpdateReachability(instance);
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal);
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

@@ -109,6 +109,8 @@ static int shm_poll(RCL_Instance instance)
shm->valid = 0;
RCL_UpdateReachability(instance);
receive_ts.tv_sec = t.receiveTimeStampSec;
clock_ts.tv_sec = t.clockTimeStampSec;

View File

@@ -129,6 +129,8 @@ static void read_sample(int sockfd, int event, void *anything)
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&sys_ts);
RCL_UpdateReachability(instance);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return;

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;

1
siv.h
View File

@@ -36,6 +36,7 @@
/* Identifiers of SIV algorithms following the IANA AEAD registry */
typedef enum {
AEAD_SIV_INVALID = 0,
AEAD_AES_SIV_CMAC_256 = 15,
AEAD_AES_SIV_CMAC_384 = 16,
AEAD_AES_SIV_CMAC_512 = 17,

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,25 +603,40 @@ error:
/* ================================================== */
static socklen_t
set_unix_sockaddr(struct sockaddr_un *sun, const char *addr)
{
size_t len = strlen(addr);
if (len + 1 > sizeof (sun->sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr);
return 0;
}
memset(sun, 0, sizeof (*sun));
sun->sun_family = AF_UNIX;
memcpy(sun->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;
}
@@ -628,17 +656,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 +877,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 +893,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 +1081,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 +1172,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);

155
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
@@ -66,7 +66,7 @@ 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 */
@@ -144,9 +144,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];
};
/* ================================================== */
@@ -206,8 +206,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);
@@ -322,9 +321,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 +338,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);
}
@@ -360,8 +358,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);
@@ -620,20 +618,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 +666,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 +711,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;
@@ -731,6 +756,43 @@ 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_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 +801,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 +812,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,7 +935,7 @@ 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 orphan_stratum, orphan_source;
@@ -881,7 +952,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 +964,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,6 +987,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* Count unreachable sources for a warning message */
if (sources[i]->reachability == 0)
n_unreach_sources++;
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now,
&si->lo_limit, &si->hi_limit, &si->root_distance,
@@ -1068,7 +1144,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 +1158,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 +1233,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 +1300,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);
@@ -1329,19 +1413,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;

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
@@ -64,6 +68,7 @@
#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 */
@@ -902,33 +907,100 @@ 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)
{
#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, O_RDONLY);
#else
return -1;
#endif
}
/* ================================================== */
int
SYS_Linux_OpenPHC(const char *device)
{
int phc_fd = -1;
if (device[0] == '/') {
phc_fd = open(device, O_RDONLY);
if (phc_fd >= 0)
phc_fd = verify_fd_is_phc(phc_fd);
}
if (phc_fd < 0) {
phc_fd = open_phc_by_iface_name(device);
if (phc_fd < 0) {
LOG(LOGS_ERR, "Could not open PHC of iface %s : %s",
device, strerror(errno));
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);
extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec tss[][3]);

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
@@ -89,9 +100,9 @@ Root delay : 0\.000000001 seconds
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
min_sync_time=80
max_sync_time=180
chronyc_start=220
min_sync_time=180
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,60 @@ 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
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

@@ -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"
@@ -313,4 +330,31 @@ check_sync && test_fail
check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail
check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
for server_aead in "" "15" "30"; do
for client_aead in "" "15" "30"; do
server_conf="
ntsaeads $server_aead
ntsserverkey tmp/server1.key
ntsservercert tmp/server1.crt
ntsprocesses 0"
client_conf="
nosystemcert
ntsaeads $client_aead
ntstrustedcerts tmp/server1.crt
ntstrustedcerts tmp/server2.crt"
client_server_conf=""
run_test || test_fail
check_chronyd_exit || test_fail
if [ -n "$server_aead" ] && [ "$server_aead" == "$client_aead" ] &&
( [ "$server_aead" != "30" ] || check_config_h '.*_SIV_GCM 1' ); then
check_source_selection || test_fail
check_sync || test_fail
else
check_source_selection && test_fail
check_sync && test_fail
fi
done
done
test_pass

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

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,8 +209,9 @@ get_free_port() {
}
generate_chrony_conf() {
local ntpport cmdport
local user ntpport cmdport
user=$(get_user)
ntpport=$(get_free_port)
cmdport=$(get_free_port)
@@ -246,14 +259,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 +277,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 +329,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 +375,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 +385,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" $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

View File

@@ -1,6 +1,6 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2017
* Copyright (C) Miroslav Lichvar 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
@@ -19,14 +19,14 @@
*/
#include <config.h>
#include <local.h>
#include "test.h"
#if defined(FEAT_NTP) || defined(FEAT_CMDMON)
#include <keys.c>
#define KEYS 100
#define KEYFILE "keys.test-keys"
#define MIN_TIMING_INTERVAL 1.0e-3
static
uint32_t write_random_key(FILE *f)
@@ -99,9 +99,11 @@ generate_key_file(const char *name, uint32_t *keys)
void
test_unit(void)
{
int i, j, data_len, auth_len, type, bits;
int i, j, data_len, auth_len, type, bits, s, timing_fails, timing_iters;
uint32_t keys[KEYS], key;
unsigned char data[100], auth[MAX_HASH_LENGTH];
unsigned char data[100], auth[MAX_HASH_LENGTH], auth2[MAX_HASH_LENGTH];
struct timespec ts1, ts2;
double diff1, diff2;
char conf[][100] = {
"keyfile "KEYFILE
};
@@ -109,6 +111,7 @@ test_unit(void)
CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
LCL_Initialise();
generate_key_file(KEYFILE, keys);
KEY_Initialise();
@@ -158,16 +161,63 @@ test_unit(void)
}
}
if (!getenv("NO_TIMING_TESTS") &&
LCL_GetSysPrecisionAsQuantum() < MIN_TIMING_INTERVAL / 100.0) {
auth_len = sizeof (auth);
UTI_GetRandomBytes(auth, auth_len);
memcpy(auth2, auth, auth_len);
timing_fails = 0;
timing_iters = 1000;
i = 0;
for (i = 0; i < 100; i++) {
int d = random() % 2;
auth2[0] = auth[0] + d;
for (j = s = 0; j < timing_iters; j++) {
if (j == 100)
LCL_ReadRawTime(&ts1);
s += UTI_IsMemoryEqual(auth, auth2, auth_len);
}
LCL_ReadRawTime(&ts2);
TEST_CHECK(s == (d + 1) % 2 * timing_iters);
diff1 = UTI_DiffTimespecsToDouble(&ts2, &ts1);
auth2[0] = auth[0] + (d + 1) % 2;
for (j = s = 0; j < timing_iters; j++) {
if (j == 100)
LCL_ReadRawTime(&ts1);
s += UTI_IsMemoryEqual(auth, auth2, auth_len);
}
LCL_ReadRawTime(&ts2);
TEST_CHECK(s == d * timing_iters);
diff2 = UTI_DiffTimespecsToDouble(&ts2, &ts1);
DEBUG_LOG("d=%d diff1=%e diff2=%e iters=%d", d, diff1, diff2, timing_iters);
if (diff1 < MIN_TIMING_INTERVAL && diff2 < MIN_TIMING_INTERVAL) {
if (timing_iters >= INT_MAX / 2)
break;
timing_iters *= 2;
i--;
continue;
}
if ((d == 0 && 0.8 * diff1 > diff2) || (d == 1 && diff1 < 0.8 * diff2))
timing_fails++;
}
DEBUG_LOG("timing fails %d/%d", timing_fails, i);
TEST_CHECK(timing_fails < i / 2);
}
unlink(KEYFILE);
KEY_Finalise();
LCL_Finalise();
CNF_Finalise();
HSH_Finalise();
}
#else
void
test_unit(void)
{
TEST_REQUIRE(0);
}
#endif

View File

@@ -29,8 +29,6 @@
#include <socket.h>
#include "test.h"
#ifdef FEAT_NTP
#include <ntp_auth.c>
static void
@@ -279,11 +277,3 @@ test_unit(void)
CNF_Finalise();
HSH_Finalise();
}
#else
void
test_unit(void)
{
TEST_REQUIRE(0);
}
#endif

View File

@@ -29,8 +29,6 @@
#include <local.h>
#include "test.h"
#ifdef FEAT_NTP
static struct timespec current_time;
static NTP_Packet req_buffer, res_buffer;
static int req_length, res_length;
@@ -638,11 +636,3 @@ test_unit(void)
CNF_Finalise();
HSH_Finalise();
}
#else
void
test_unit(void)
{
TEST_REQUIRE(0);
}
#endif

View File

@@ -21,8 +21,6 @@
#include <config.h>
#include "test.h"
#ifdef FEAT_NTP
#include <util.h>
#include <logging.h>
@@ -157,11 +155,3 @@ test_unit(void)
}
}
#else
void
test_unit(void)
{
TEST_REQUIRE(0);
}
#endif

View File

@@ -21,8 +21,6 @@
#include <config.h>
#include "test.h"
#ifdef FEAT_NTP
#include <conf.h>
#include <cmdparse.h>
#include <nameserv_async.h>
@@ -372,11 +370,3 @@ test_unit(void)
CNF_Finalise();
HSH_Finalise();
}
#else
void
test_unit(void)
{
TEST_REQUIRE(0);
}
#endif

View File

@@ -1,6 +1,6 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
* Copyright (C) Miroslav Lichvar 2020, 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
@@ -23,8 +23,24 @@
#ifdef FEAT_NTS
#include <nts_ke_client.c>
#include <local.h>
#include <nts_ke_session.h>
#include <util.h>
#define NKSN_GetKeys get_keys
static int
get_keys(NKSN_Instance session, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{
c2s->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(c2s->key, c2s->length);
s2c->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(s2c->key, s2c->length);
return 1;
}
#include <nts_ke_client.c>
static void
prepare_response(NKSN_Instance session, int valid)
@@ -88,12 +104,17 @@ prepare_response(NKSN_Instance session, int valid)
if (random() % 2) {
length = random() % (sizeof (data) + 1);
TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
}
if (random() % 2)
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
if (index != 8) {
for (i = 0; i < NKE_MAX_COOKIES; i++) {
for (i = random() % NKE_MAX_COOKIES; i >= 0; i--) {
length = (random() % sizeof (data) + 1) / 4 * 4;
if (i == 0 && length == 0)
length = 4;
if (index == 9)
length += (length < sizeof (data) ? 1 : -1) * (random() % 3 + 1);
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COOKIE, data, length));
@@ -103,12 +124,16 @@ prepare_response(NKSN_Instance session, int valid)
TEST_CHECK(NKSN_EndMessage(session));
}
#define MAX_COOKIES (2 * NKE_MAX_COOKIES)
void
test_unit(void)
{
NKE_Context context, alt_context;
NKE_Cookie cookies[MAX_COOKIES];
int i, j, r, valid, num_cookies;
NKC_Instance inst;
IPSockAddr addr;
int i, r, valid;
char conf[][100] = {
"nosystemcert",
@@ -127,10 +152,33 @@ test_unit(void)
TEST_CHECK(inst);
for (i = 0; i < 10000; i++) {
inst->got_response = 0;
valid = random() % 2;
prepare_response(inst->session, valid);
r = process_response(inst);
r = handle_message(inst);
TEST_CHECK(r == valid);
memset(&context, 0, sizeof (context));
memset(&alt_context, 0, sizeof (alt_context));
num_cookies = 0;
r = NKC_GetNtsData(inst, &context, &alt_context,
cookies, &num_cookies, random() % MAX_COOKIES + 1, &addr);
TEST_CHECK(r == valid);
if (r) {
TEST_CHECK(context.algorithm != AEAD_SIV_INVALID);
TEST_CHECK(context.c2s.length > 0);
TEST_CHECK(context.c2s.length == SIV_GetKeyLength(context.algorithm));
TEST_CHECK(context.s2c.length == SIV_GetKeyLength(context.algorithm));
if (alt_context.algorithm != AEAD_SIV_INVALID) {
TEST_CHECK(context.c2s.length > 0);
TEST_CHECK(alt_context.c2s.length == SIV_GetKeyLength(alt_context.algorithm));
TEST_CHECK(alt_context.s2c.length == SIV_GetKeyLength(alt_context.algorithm));
}
TEST_CHECK(num_cookies > 0 && num_cookies <= NKE_MAX_COOKIES);
for (j = 0; j < num_cookies; j++)
TEST_CHECK(cookies[j].length > 0 && cookies[j].length % 4 == 0);
}
}
NKC_DestroyInstance(inst);

View File

@@ -30,11 +30,12 @@
#define NKSN_GetKeys get_keys
static int
get_keys(NKSN_Instance session, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
get_keys(NKSN_Instance session, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
{
c2s->length = SIV_GetKeyLength(siv);
c2s->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(c2s->key, c2s->length);
s2c->length = SIV_GetKeyLength(siv);
s2c->length = SIV_GetKeyLength(algorithm);
UTI_GetRandomBytes(s2c->key, s2c->length);
return 1;
}
@@ -91,7 +92,7 @@ prepare_request(NKSN_Instance session, int valid)
if (index == 8) {
length = random() % (sizeof (data) + 1);
TEST_CHECK(NKSN_AddRecord(session, 1, 1000 + random() % 1000, data, length));
TEST_CHECK(NKSN_AddRecord(session, 1, 2000 + random() % 1000, data, length));
}
if (random() % 2) {
@@ -105,9 +106,12 @@ prepare_request(NKSN_Instance session, int valid)
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_NTPV4_PORT_NEGOTIATION, data, length));
}
if (random() % 2)
TEST_CHECK(NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0));
if (random() % 2) {
length = random() % (sizeof (data) + 1);
TEST_CHECK(NKSN_AddRecord(session, 0, 1000 + random() % 1000, data, length));
TEST_CHECK(NKSN_AddRecord(session, 0, 2000 + random() % 1000, data, length));
}
TEST_CHECK(NKSN_EndMessage(session));
@@ -174,7 +178,8 @@ test_unit(void)
for (i = 0; i < 10000; i++) {
context.algorithm = AEAD_AES_SIV_CMAC_256;
get_keys(session, context.algorithm, &context.c2s, &context.s2c);
get_keys(session, context.algorithm, random() % 100, NKE_NEXT_PROTOCOL_NTPV4,
&context.c2s, &context.s2c);
memset(&cookie, 0, sizeof (cookie));
TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
TEST_CHECK(NKS_DecodeCookie(&cookie, &context2));

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