Compare commits

...

165 Commits

Author SHA1 Message Date
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
Miroslav Lichvar
113d1134d1 doc: update NEWS 2024-08-29 10:28:49 +02:00
Miroslav Lichvar
b363af754d doc: update README 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
0f5cf57bc2 update copyright years 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
5a43f0c39b test: make 110-chronyc more reliable 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
5a6fbe7a4b test: make 106-refclock more reliable 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
bb34e92f96 test: make 108-peer more reliable 2024-08-29 09:37:50 +02:00
Miroslav Lichvar
78b9c13a11 sources: replace unreachable sources before selection
The commit c43efccf02 ("sources: update source selection with
unreachable sources") caused a high rate of failures in the
148-replacement test (1 falseticker vs 2 unreachable sources). This was
due to a larger fraction of the replacement attempts being made for the
source incorrectly marked as a falseticker instead of the second
unreachable source and the random process needed more time to get to the
expected state with both unreachable sources replaced.

When updating reachability of an unreachable source, try to request the
replacement of the source before calling the source selection, where
other sources may be replaced, to better balance the different
replacement attempts.
2024-08-29 09:37:42 +02:00
Luca Boccassi
1ab5b88939 conf: do not check, write and delete PID file if set to '/'
If the pid file path is specified as '/', skip handling it,
as it is not only unnecessary but complicates managing the
service. A systemd unit can manage the program without any
need for this functionality, and it makes process tracking
simpler and more robust.
The implementation matches the bindcmdaddress directive.
2024-08-22 14:24:49 +02:00
Luca Boccassi
e30f937f6a doc: explain how to disable DNSSEC validation with sd-resolved in FAQ
DNSSEC requires the system time to be synced in order to work,
as the signature date and expiration need to be checked by
resolvers. But it is possible that syncing the times requires
doing DNS queries. Add a paragraph to the FAQ explaining how
to break this cycle by asking nss-resolved to always avoid
DNSSEC when chronyd tries to resolve hostnames.
2024-08-22 14:24:49 +02:00
Miroslav Lichvar
08b67dba98 ntp: fix finalization for async resolver
If an attempt to resolve addresses of an NTP server is made right before
starting the termination sequence, the asynchronous resolver thread
could read the server name when it was already freed.

Leave unresolved sources allocated in NSR_Finalise() if the async
resolver did not finish yet, at least for now. Waiting for the resolving
result or cancelling the thread would complicate the code. The scheduler
is not expected to be running at this point.
2024-08-22 09:32:36 +02:00
Miroslav Lichvar
61f15fedcd doc: add new question about accuracy to FAQ 2024-08-21 11:24:24 +02:00
Miroslav Lichvar
6d59234995 doc: clarify hostname with nts option 2024-08-20 14:18:54 +02:00
Miroslav Lichvar
d4a4f89329 conf: don't repeat error message when adding sourcedir source
When a source from a configured sourcedir cannot be added (e.g. it is a
duplicate of another source), log the error message only on the first
attempt adding the source, until the source is removed and added to a
sourcedir again.

This avoids spamming of the system log with error messages if the
reload sources command is called frequently (e.g. from a DHCP renewal
networking script).
2024-08-07 09:48:24 +02:00
Miroslav Lichvar
916ed70c4a conf: save source status in sourcedir reload
Save the NSR status when adding a source from a sourcedir and don't
hide sources that failed the addition by clearing their name.
2024-08-07 09:48:13 +02:00
Miroslav Lichvar
8ba2da52df conf: merge ntp_source_ids with ntp_sources
Keep the configuration IDs of sources loaded from sourcedir in the
NTP_Source structure itself to simplify the code.
2024-08-07 09:46:00 +02:00
Miroslav Lichvar
fd9e956d27 test: extend 008-confload test 2024-08-06 11:27:13 +02:00
Miroslav Lichvar
43189651b0 doc: update NEWS 2024-07-30 14:05:42 +02:00
Miroslav Lichvar
f518b8d00f doc: update README 2024-07-30 12:11:09 +02:00
Miroslav Lichvar
42b3c40c32 doc: fix typo in kod option description 2024-07-30 12:11:09 +02:00
Miroslav Lichvar
66512ebcb3 ntp: make sure new configuration IDs are unused
The configuration IDs assigned to individual sources (used when they
don't have a resolved IP address) and pools of sources are 32-bit. The
ID could overflow if some sources were very frequently removed and added
again. Two unrelated sources could end up with the same ID, causing some
operations to unexpectedly impact only one or both sources.

Make sure the ID is currently unused before assigning it to a new source.
2024-07-30 12:11:09 +02:00
Miroslav Lichvar
3940d2aae3 leapdb: add explicit cast to int64_t
Add an explicit cast to int64_t to not rely on LEAP_SEC_LIST_OFFSET
not fitting in 32-bit time_t.
2024-07-30 12:09:53 +02:00
Miroslav Lichvar
34be117c9c main: check for killed foreground process
On start, if the foreground process waiting for the daemon process to
close the pipe (after finishing the RTC initialization, initstepslew,
etc) is killed, terminate the daemon too assuming that whatever killed
the foreground process it wanted all chronyd processes to stop.

In the daemon, before closing the pipe file descriptor, send an empty
message to check if the pipe isn't already closed on the other end.
2024-07-04 16:50:22 +02:00
Miroslav Lichvar
7915f52495 logging: add function to send message to foreground process 2024-07-04 16:50:22 +02:00
Miroslav Lichvar
05bd4898a9 test: fix 142-ntpoverptp 2024-06-20 15:10:42 +02:00
Miroslav Lichvar
4da088ec2f ntp: make NTP-over-PTP domain configurable
Add ptpdomain directive to set the domain number of transmitted and
accepted NTP-over-PTP messages. It might need to be changed in networks
using a PTP profile with the same domain number. The default domain
number of 123 follows the current NTP-over-PTP specification.
2024-06-20 15:00:17 +02:00
Miroslav Lichvar
c46e0549ab ntp: update NTP-over-PTP support
Following the latest version of the draft, accept NTP messages in both
PTPv2 and PTPv2.1 messages, accept sync messages in addition to delay
request messages, and check the minorSdoId field in PTPv2.1 messages.

Transmitted messages are still PTPv2 delay requests.

Don't switch to the organization-specific TLV yet. Wait for the NTP TLV
subtype and Network Correction extension field to be assigned by IANA to
avoid an additional break in compatibility.
2024-06-20 14:35:28 +02:00
Miroslav Lichvar
8f5b308414 test: make 124-tai more reliable
Reported-by: Reinhard Max <max@suse.de>
2024-06-04 16:25:55 +02:00
Miroslav Lichvar
084fe6b0cc doc: clarify prefer source option 2024-06-04 16:25:55 +02:00
Miroslav Lichvar
ebfc676d74 ntp: limit offset correction to supported NTP interval
When an NTP source is specified with the offset option, the corrected
offset may get outside of the supported NTP interval (by default -50..86
years around the build date). If the source passed the source selection,
the offset would be rejected only later in the adjustment of the local
clock.

Check the offset validity as part of the NTP test A to make the source
unselectable and make it visible in the measurements log and ntpdata
report.
2024-05-02 14:43:51 +02:00
Miroslav Lichvar
adaca0ff19 reference: switch is_leap_close() from time_t to double
Avoid undefined behavior in the timestamp conversion from double to
time_t in REF_IsLeapSecondClose() with NTP sources configured with a
large offset correction.
2024-05-02 14:43:46 +02:00
Miroslav Lichvar
84d6c7a527 sources: allow logging one selection failure on start
Allow one message about failed selection (e.g. no selectable sources)
to be logged before first successful selection when a source has
full-size reachability register (8 polls with a received or missed
response).

This should make it more obvious that chronyd has a wrong configuration
or there is a firewall/networking issue.
2024-05-02 12:51:38 +02:00
Miroslav Lichvar
c43efccf02 sources: update source selection with unreachable sources
When updating the reachability register of a source with zero, call the
source selection even if the source is not the currently selected as the
best source. But do that only if all reachability bits are zero, i.e.
there was no synchronized response for last 8 polls.

This will enable the source selection to log a message when only
unreachable sources are updating reachability and it decreases the
number of unnecessary source selections.
2024-04-30 15:52:18 +02:00
Miroslav Lichvar
1affd03cca sources: reorder unsynchronised source status
In the source selection, check for the unsynchronized leap status after
getting sourcestats data. The unsynchronized source status is supposed
to indicate an unsynchronized source that is providing samples, not a
source which doesn't have any samples.

Also, fix the comment describing the status.

Fixes: 4c29f8888c ("sources: handle unsynchronized sources in selection")
2024-04-30 15:52:18 +02:00
Miroslav Lichvar
276591172e ntp: improve copying of server status
When a server specified with the copy option responds with an
unsynchronized status (e.g. due to selection failure), reset the
source instance to immediately switch the local reference status
instead of waiting for the source to become unreachable after 8 polls.
2024-04-29 11:21:45 +02:00
Rob Gill
989ef702aa doc: fix typo in README
Typo correction only - no change to content

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2024-04-16 07:50:11 +02:00
Rob Gill
1920b1efde doc: fix typo in chronyc docs
Typo fix only - no change to content

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2024-04-16 07:49:59 +02:00
Miroslav Lichvar
bb5db828c6 ntp: log failed connection to Samba signd socket
Log an error message (in addition to the socket-specific debug message)
when the connection to signd socket fails, but only once before a
successful signd exchange to avoid flooding the system log.
2024-04-15 16:35:33 +02:00
Miroslav Lichvar
dcc94a4c10 doc: add contributing.adoc 2024-04-11 16:52:06 +02:00
Miroslav Lichvar
2ed72c49c9 test: add --enable-debug option to 002-scanbuild 2024-04-11 12:53:01 +02:00
Miroslav Lichvar
342b588e3b avoid some static analysis errors
Modify the code to avoid some false positives reported by the clang and
gcc static analyzers.
2024-04-11 10:31:02 +02:00
Miroslav Lichvar
a914140bd4 sys_linux: disable other external timestamping channels
Use new ioctls added in Linux 6.7 to disable receiving events from other
channels when enabling external timestamping on a PHC. This should save
some CPU time when other applications or chronyd instances are using
other channels of the same PHC.
2024-04-10 15:33:04 +02:00
Miroslav Lichvar
28e4eec1c4 refclock: update comment in PHC driver
Since Linux 6.7 external timestamping events are no longer shared among
all descriptors of a PHC. Each descriptor gets its own copy of each
timestamp.
2024-04-10 12:13:29 +02:00
Miroslav Lichvar
5235c51801 cmdmon: add reserved fields to local command
Add two reserved fields initialized to zero to the new REQ_LOCAL3
command to allow adding more options (e.g. delay in activation) without
changing the command number again.
2024-04-04 16:24:43 +02:00
Miroslav Lichvar
26ea4e35e7 test: add tests of local directive options 2024-04-04 16:24:02 +02:00
Andy Fiddaman
9397ae2b0a reference: add "local activate" option
This option sets an activating root distance for the local reference. The
local reference will not be used until the root distance drops below the
configured value for the first time. This can be used to prevent the local
reference from being activated on a server which has never been synchronised
with an upstream server. The default value of 0.0 causes no activating
distance to be used, such that the local reference is always eligible for
activation.
2024-04-04 15:17:05 +02:00
Miroslav Lichvar
b8ead3485b leapdb: fix leapsec list processing with 32-bit time_t
A 32-bit time_t value overflows when converted to the Y1900 epoch used
in the leapsec list. Use a 64-bit variable in get_list_leap() to fix the
comparisons on systems using 32-bit time_t.

Fixes: 53823b9f1c ("leapdb: support leap-seconds.list as second source")
2024-04-03 11:01:44 +02:00
Miroslav Lichvar
24d28cd679 ntp: add server support for KoD RATE
Add "kod" option to the ratelimit directive to respond with the KoD
RATE code to randomly selected requests exceeding the configured limit.
This complements the client support of KoD RATE. It's disabled by
default.

There can be only one KoD code in one response. If both NTS NAK and RATE
codes are triggered, drop the response. The KoD RATE code can be set in
an NTS-authenticated response.
2024-04-02 15:39:12 +02:00
Miroslav Lichvar
aac898343e clientlog: add support for KoD rate limiting
Add a third return value to CLG_LimitServiceRate() to indicate the
server should send a response requesting the client to reduce its
polling rate. It randomly selects from a fraction (configurable to 1/2,
1/4, 1/8, 1/16, or disabled) of responses which would be dropped
(after selecting responses for the leak option).
2024-04-02 15:23:26 +02:00
Miroslav Lichvar
c8c7f518b1 clientlog: return enum from CLG_LimitServiceRate()
Change CLG_LimitServiceRate() to return an enum in preparation for
adding KoD RATE support.
2024-04-02 11:55:02 +02:00
Miroslav Lichvar
ce956c99a8 nts: check for NTS NAK specifically when responding
Ignore other KoD codes than NTS NAK when deciding if the server response
should not be authenticated.
2024-04-02 11:33:04 +02:00
Miroslav Lichvar
863866354d ntp: avoid unnecessary restart of resolving round on refresh
Don't call NSR_ResolveSources() when a resolving round is already
started. This cuts the number of calls of the system resolver made due
to the refresh command to half.
2024-03-14 16:39:41 +01:00
Miroslav Lichvar
6e5513c80b ntp: don't keep refresh requests in list of unresolved sources
The refresh command adds requests to reresolve addresses of all sources.
If some sources didn't have an IP address resolved yet, the
corresponding requests were not removed after failed resolving. Repeated
refresh commands increased the number of requests and number of calls of
the system resolver, which might not be caching DNS responses.

Remove all refresh requests from the list after resolving attempt to fix
that.

Reported-by: t.barnewski@avm.de
Fixes: d7e3ad17ff ("ntp: create sources for unresolved addresses")
2024-03-14 16:39:41 +01:00
Miroslav Lichvar
6d0143e963 ntp: add more debug messages for resolving 2024-03-14 16:39:32 +01:00
Miroslav Lichvar
f49be7f063 conf: don't load sourcedir during initstepslew and RTC init
If the reload sources command was received in the chronyd start-up
sequence with initstepslew and/or RTC init (-s option), the sources
loaded from sourcedirs caused a crash due to failed assertion after
adding sources specified in the config.

Ignore the reload sources command until chronyd enters the normal
operation mode.

Fixes: 519796de37 ("conf: add sourcedirs directive")
2024-03-12 14:57:30 +01:00
Miroslav Lichvar
7fe98a83b8 test: replace another C99-style declaration in for loop 2024-03-11 12:00:12 +01:00
Miroslav Lichvar
ad37c409c9 cmdmon: add offset command
Add a new command to modify the offset option of NTP sources and
reference clocks.
2024-03-07 16:20:27 +01:00
Miroslav Lichvar
719c6f6a8a ntp+refclock: add functions to modify offset option 2024-03-07 16:19:04 +01:00
Miroslav Lichvar
b0750136b5 rtc+getdate: initialize tm_wday for mktime()
Even though mktime() is documented as ignoring the tm_wday field, the
coverity static analyzer complains about passing an uninitialized value.
Set the field to zero to make it happy.
2024-03-04 11:38:16 +01:00
Miroslav Lichvar
ad79aec946 test: avoid C99-style declaration in for loop
This fixes compilation without the -std=c99 option with an older gcc.
2024-03-04 11:38:11 +01:00
Miroslav Lichvar
008dc16727 examples: switch chrony.conf examples to leapseclist 2024-02-08 16:21:42 +01:00
Miroslav Lichvar
6cf9fe2f16 test: improve 113-leapsecond and 124-tai tests
Use leapseclist instead of leapsectz and test also negative leap
seconds. Add a test for leapsectz when the date command indicates
right/UTC is available on the system and mktime() works as expected.
Check TAI offset in the server's log.
2024-02-08 15:54:24 +01:00
Patrick Oppenlander
637b77d1bd test: add leapdb unit test 2024-02-08 15:54:24 +01:00
Patrick Oppenlander
53823b9f1c leapdb: support leap-seconds.list as second source
The existing implementation of getting leap second information from a
timezone in get_tz_leap() relies on non-portable C library behaviour.

Specifically, mktime is not required to return '60' in the tm_sec field
when a leap second is inserted leading to "Timezone right/UTC failed
leap second check, ignoring" errors on musl based systems.

This patch adds support for getting leap second information from the
leap-seconds.list file included with tzdata and adds a new configuration
directive leapseclist to switch on the feature.
2024-02-08 15:54:24 +01:00
Patrick Oppenlander
83f90279b0 leapdb: move source check into separate function
The sanity checks are valid for all possible sources of leap second
information, so move them into a separate function check_leap_source().
2024-02-08 15:54:21 +01:00
Patrick Oppenlander
02ae9a8607 leapdb: make twice per day check logic common
We want to do the twice per day check regardless of the data source.
Move the check up one level from get_tz_leap() into LDB_GetLeap().
2024-02-08 12:54:37 +01:00
Patrick Oppenlander
017d6f8f56 reference: move leap second source into leapdb
Separate out source of leap second data into a new module in preparation
for supporting more sources such as leap-seconds.list.
2024-02-08 12:54:37 +01:00
Miroslav Lichvar
eb26d13140 cmdmon: add timestamp counters to ntpdata report 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
8d19f49341 ntp: add per-source counters of kernel and hardware timestamps 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
637fa29e1e cmdmon: add ipv4/ipv6 options to add source command 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
2d349595ee cmdmon: simplify flag checking in handle_add_source() 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
5cb584d6c1 conf: add ipv4 and ipv6 options to server/pool/peer directive
Accept "ipv4" and "ipv6" options in the server/pool/peer directive to
use only IPv4 or IPv6 addresses respectively.

The configuration is different from the "server [-4|-6] hostname" syntax
supported by ntpd to avoid breaking existing scripts which expect the
hostname to always be the first argument of the directives.
2024-02-07 10:23:40 +01:00
Miroslav Lichvar
d7c2b1d2f3 ntp: support per-source IP family restriction
Add a new parameter to the NSR_AddSourceByName() function to allow
individual sources to be limited to IPv4 or IPv6 addresses. This doesn't
change the options passed to the resolver. It's just an additional
filter in the processing of resolved addresses following the -4/-6
command-line option of chronyd.
2024-02-07 10:23:36 +01:00
Miroslav Lichvar
e11b518a1f ntp: fix authenticated requests in serverstats
Fix the CLG_UpdateNtpStats() call to count requests passing the
authentication check instead of requests triggering a KoD response
(i.e. NTS NAK).
2024-01-08 11:46:32 +01:00
133 changed files with 4444 additions and 2367 deletions

View File

@@ -37,7 +37,9 @@ GETDATE_CFLAGS = @GETDATE_CFLAGS@
EXTRA_OBJS = @EXTRA_OBJS@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
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)

63
NEWS
View File

@@ -1,3 +1,66 @@
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
Bug fixes
---------
* 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
==================
Enhancements
------------
* Add activate option to local directive to set activation threshold
* Add ipv4 and ipv6 options to server/pool/peer directive
* Add kod option to ratelimit directive for server KoD RATE support
* Add leapseclist directive to read NIST/IERS leap-seconds.list file
* Add ptpdomain directive to set PTP domain for NTP over PTP
* Allow disabling pidfile
* Improve copy server option to accept unsynchronised status instantly
* Log one selection failure on start
* Add offset command to modify source offset correction
* Add timestamp sources to ntpdata report
Bug fixes
---------
* Fix crash on sources reload during initstepslew or RTC initialisation
* Fix source refreshment to not repeat failed name resolving attempts
New in version 4.5
==================

10
README
View File

@@ -12,7 +12,7 @@ a time service to other computers in the network.
It is designed to perform well in a wide range of conditions, including
intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
and systems that do not run continuously, or run on a virtual machine.
Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of
@@ -75,6 +75,7 @@ Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk>
Andrew Bishop <amb@gedanken.demon.co.uk>
Vincent Blut <vincent.debian@free.fr>
Luca Boccassi <bluca@debian.org>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
@@ -83,13 +84,18 @@ 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>
Rob Gill <rrobgill@protonmail.com>
Alexander Gretencord <arutha@gmx.de>
Andrew Griffiths <agriffit@redhat.com>
Walter Haidinger <walter.haidinger@gmx.at>
@@ -111,12 +117,14 @@ Paul Menzel <paulepanter@users.sourceforge.net>
Vladimir Michl <vladimir.michl@seznam.cz>
Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Patrick Oppenlander <patrick.oppenlander@gmail.com>
Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
Shachar Raindel <shacharr@google.com>
Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il>
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>

40
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
@@ -110,7 +109,9 @@
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
#define REQ_MODIFY_SELECTOPTS 72
#define N_REQUEST_TYPES 73
#define REQ_MODIFY_OFFSET 73
#define REQ_LOCAL3 74
#define N_REQUEST_TYPES 75
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -221,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;
@@ -236,6 +232,9 @@ typedef struct {
int32_t stratum;
Float distance;
int32_t orphan;
Float activate;
Float wait_synced;
Float wait_unsynced;
int32_t EOR;
} REQ_Local;
@@ -279,6 +278,8 @@ typedef struct {
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP_MONO_ROOT 0x800
#define REQ_ADDSRC_EF_EXP_NET_CORRECTION 0x1000
#define REQ_ADDSRC_IPV4 0x2000
#define REQ_ADDSRC_IPV6 0x4000
typedef struct {
uint32_t type;
@@ -388,6 +389,13 @@ typedef struct {
int32_t EOR;
} REQ_Modify_SelectOpts;
typedef struct {
IPAddr address;
uint32_t ref_id;
Float new_offset;
int32_t EOR;
} REQ_Modify_Offset;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -474,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;
@@ -495,6 +502,7 @@ typedef struct {
REQ_AuthData auth_data;
REQ_SelectData select_data;
REQ_Modify_SelectOpts modify_select_opts;
REQ_Modify_Offset modify_offset;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -503,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 */
@@ -538,7 +539,8 @@ typedef struct {
#define RPY_SELECT_DATA 23
#define RPY_SERVER_STATS3 24
#define RPY_SERVER_STATS4 25
#define N_REPLY_TYPES 26
#define RPY_NTP_DATA2 26
#define N_REPLY_TYPES 27
/* Status codes */
#define STT_SUCCESS 0
@@ -761,7 +763,11 @@ typedef struct {
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
uint32_t reserved[3];
uint32_t total_kernel_tx_ts;
uint32_t total_kernel_rx_ts;
uint32_t total_hw_tx_ts;
uint32_t total_hw_rx_ts;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;

115
client.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2023
* Copyright (C) Miroslav Lichvar 2009-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
@@ -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);
@@ -344,6 +343,24 @@ parse_source_address(char *word, IPAddr *address)
/* ================================================== */
static int
parse_source_address_or_refid(char *s, IPAddr *address, uint32_t *ref_id)
{
address->family = IPADDR_UNSPEC;
*ref_id = 0;
/* Don't allow hostnames to avoid conflicts with reference IDs */
if (UTI_StringToIdIP(s, address) || UTI_StringToIP(s, address))
return 1;
if (CPS_ParseRefid(s, ref_id) > 0)
return 1;
return 0;
}
/* ================================================== */
static int
read_mask_address(char *line, IPAddr *mask, IPAddr *address)
{
@@ -736,23 +753,27 @@ 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;
if (!strcmp(line, "off")) {
on_off = 0;
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
} 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");
return 0;
}
msg->command = htons(REQ_LOCAL2);
msg->command = htons(REQ_LOCAL3);
msg->data.local.on_off = htonl(on_off);
msg->data.local.stratum = htonl(stratum);
msg->data.local.distance = UTI_FloatHostToNetwork(distance);
msg->data.local.orphan = htonl(orphan);
msg->data.local.activate = UTI_FloatHostToNetwork(activate);
msg->data.local.wait_synced = UTI_FloatHostToNetwork(wait_synced);
msg->data.local.wait_unsynced = UTI_FloatHostToNetwork(wait_unsynced);
return 1;
}
@@ -886,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);
@@ -908,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) ||
@@ -927,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);
@@ -962,6 +980,8 @@ process_cmd_add_source(CMD_Request *msg, char *line)
REQ_ADDSRC_EF_EXP_MONO_ROOT : 0) |
(data.params.ext_fields & NTP_EF_FLAG_EXP_NET_CORRECTION ?
REQ_ADDSRC_EF_EXP_NET_CORRECTION : 0) |
(data.family == IPADDR_INET4 ? REQ_ADDSRC_IPV4 : 0) |
(data.family == IPADDR_INET6 ? REQ_ADDSRC_IPV6 : 0) |
convert_addsrc_sel_options(data.params.sel_options));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
@@ -971,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;
}
@@ -1029,6 +1058,7 @@ give_help(void)
"selectopts <address|refid> <+|-options>\0Modify selection options\0"
"reselect\0Force reselecting synchronisation source\0"
"reselectdist <dist>\0Modify reselection distance\0"
"offset <address|refid> <offset>\0Modify offset correction\0"
"\0\0"
"NTP sources:\0\0"
"activity\0Check how many NTP sources are online/offline\0"
@@ -1139,7 +1169,8 @@ command_name_generator(const char *text, int state)
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"maxupdateskew", "minpoll", "minstratum", "ntpdata",
"offline", "offset", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
"retries", "rtcdata", "selectdata", "selectopts", "serverstats", "settime",
"shutdown", "smoothing", "smoothtime", "sourcename", "sources", "sourcestats",
@@ -2329,7 +2360,7 @@ process_cmd_ntpdata(char *line)
request.command = htons(REQ_NTP_DATA);
UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
if (!request_reply(&request, &reply, RPY_NTP_DATA2, 0))
return 0;
UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
@@ -2365,7 +2396,11 @@ process_cmd_ntpdata(char *line)
"Total TX : %U\n"
"Total RX : %U\n"
"Total valid RX : %U\n"
"Total good RX : %U\n",
"Total good RX : %U\n"
"Total kernel TX : %U\n"
"Total kernel RX : %U\n"
"Total HW TX : %U\n"
"Total HW RX : %U\n",
UTI_IPToString(&remote_addr), UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port),
UTI_IPToString(&local_addr), UTI_IPToRefid(&local_addr),
@@ -2393,6 +2428,10 @@ process_cmd_ntpdata(char *line)
ntohl(reply.data.ntp_data.total_rx_count),
ntohl(reply.data.ntp_data.total_valid_count),
ntohl(reply.data.ntp_data.total_good_count),
ntohl(reply.data.ntp_data.total_kernel_tx_ts),
ntohl(reply.data.ntp_data.total_kernel_rx_ts),
ntohl(reply.data.ntp_data.total_hw_tx_ts),
ntohl(reply.data.ntp_data.total_hw_rx_ts),
REPORT_END);
}
@@ -2848,6 +2887,34 @@ process_cmd_activity(const char *line)
/* ================================================== */
static int
process_cmd_offset(CMD_Request *msg, char *line)
{
uint32_t ref_id;
IPAddr ip_addr;
double offset;
char *src;
src = line;
line = CPS_SplitWord(line);
if (!parse_source_address_or_refid(src, &ip_addr, &ref_id) ||
sscanf(line, "%lf", &offset) != 1) {
LOG(LOGS_ERR, "Invalid syntax for offset command");
return 0;
}
UTI_IPHostToNetwork(&ip_addr, &msg->data.modify_offset.address);
msg->data.modify_offset.ref_id = htonl(ref_id);
msg->data.modify_offset.new_offset = UTI_FloatHostToNetwork(offset);
msg->command = htons(REQ_MODIFY_OFFSET);
return 1;
}
/* ================================================== */
static int
process_cmd_reselectdist(CMD_Request *msg, char *line)
{
@@ -2929,15 +2996,10 @@ process_cmd_selectopts(CMD_Request *msg, char *line)
src = line;
line = CPS_SplitWord(line);
ref_id = 0;
/* Don't allow hostnames to avoid conflicts with reference IDs */
if (!UTI_StringToIdIP(src, &ip_addr) && !UTI_StringToIP(src, &ip_addr)) {
ip_addr.family = IPADDR_UNSPEC;
if (CPS_ParseRefid(src, &ref_id) == 0) {
LOG(LOGS_ERR, "Invalid syntax for selectopts command");
return 0;
}
if (!parse_source_address_or_refid(src, &ip_addr, &ref_id)) {
LOG(LOGS_ERR, "Invalid syntax for selectopts command");
return 0;
}
mask = options = 0;
@@ -3239,6 +3301,8 @@ process_line(char *line)
ret = process_cmd_ntpdata(line);
} else if (!strcmp(command, "offline")) {
do_normal_submit = process_cmd_offline(&tx_message, line);
} else if (!strcmp(command, "offset")) {
do_normal_submit = process_cmd_offset(&tx_message, line);
} else if (!strcmp(command, "online")) {
do_normal_submit = process_cmd_online(&tx_message, line);
} else if (!strcmp(command, "onoffline")) {
@@ -3383,7 +3447,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2023 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2024 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",
@@ -3531,6 +3595,7 @@ main(int argc, char **argv)
close_io();
free_addresses(server_addresses);
SCK_Finalise();
UTI_ResetGetRandomFunctions();
return !ret;
}

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 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
@@ -117,6 +117,14 @@ static int token_shift[MAX_SERVICES];
static int leak_rate[MAX_SERVICES];
/* Rates at which responses requesting clients to reduce their rate
(e.g. NTP KoD RATE) are randomly allowed (in log2, but 0 means disabled) */
#define MIN_KOD_RATE 0
#define MAX_KOD_RATE 4
static int kod_rate[MAX_SERVICES];
/* Limit intervals in log2 */
static int limit_interval[MAX_SERVICES];
@@ -257,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;
@@ -354,18 +359,19 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void
CLG_Initialise(void)
{
int i, interval, burst, lrate, slots2;
int i, interval, burst, lrate, krate, slots2;
for (i = 0; i < MAX_SERVICES; i++) {
max_tokens[i] = 0;
tokens_per_hit[i] = 0;
token_shift[i] = 0;
leak_rate[i] = 0;
kod_rate[i] = 0;
limit_interval[i] = MIN_LIMIT_INTERVAL;
switch (i) {
case CLG_NTP:
if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate, &krate))
continue;
break;
case CLG_NTSKE:
@@ -382,6 +388,7 @@ CLG_Initialise(void)
set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
kod_rate[i] = CLAMP(MIN_KOD_RATE, krate, MAX_KOD_RATE);
limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
}
@@ -579,28 +586,28 @@ CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
/* ================================================== */
static int
limit_response_random(int leak_rate)
limit_response_random(int rate)
{
static uint32_t rnd;
static int bits_left = 0;
int r;
if (bits_left < leak_rate) {
if (bits_left < rate) {
UTI_GetRandomBytes(&rnd, sizeof (rnd));
bits_left = 8 * sizeof (rnd);
}
/* Return zero on average once per 2^leak_rate */
r = rnd % (1U << leak_rate) ? 1 : 0;
rnd >>= leak_rate;
bits_left -= leak_rate;
/* Return zero on average once per 2^rate */
r = rnd % (1U << rate) ? 1 : 0;
rnd >>= rate;
bits_left -= rate;
return r;
}
/* ================================================== */
int
CLG_Limit
CLG_LimitServiceRate(CLG_Service service, int index)
{
Record *record;
@@ -609,14 +616,14 @@ CLG_LimitServiceRate(CLG_Service service, int index)
check_service_number(service);
if (tokens_per_hit[service] == 0)
return 0;
return CLG_PASS;
record = ARR_GetElement(records, index);
record->drop_flags &= ~(1U << service);
if (record->tokens[service] >= tokens_per_hit[service]) {
record->tokens[service] -= tokens_per_hit[service];
return 0;
return CLG_PASS;
}
drop = limit_response_random(leak_rate[service]);
@@ -632,14 +639,18 @@ CLG_LimitServiceRate(CLG_Service service, int index)
if (!drop) {
record->tokens[service] = 0;
return 0;
return CLG_PASS;
}
if (kod_rate[service] > 0 && !limit_response_random(kod_rate[service])) {
return CLG_KOD;
}
record->drop_flags |= 1U << service;
record->drops[service]++;
total_drops[service]++;
return 1;
return CLG_DROP;
}
/* ================================================== */

View File

@@ -37,11 +37,17 @@ typedef enum {
CLG_CMDMON,
} CLG_Service;
typedef enum {
CLG_PASS = 0,
CLG_DROP,
CLG_KOD,
} CLG_Limit;
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern int CLG_LimitServiceRate(CLG_Service service, int index);
extern CLG_Limit CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
NTP_Timestamp_Source tx_ts_src);
extern int CLG_GetNtpMinPoll(void);

727
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2023
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-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
@@ -68,85 +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 */
};
/* ================================================== */
/* This authorisation table is used for checking whether particular
@@ -225,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));
}
}
@@ -247,7 +166,6 @@ void
CAM_Initialise(void)
{
assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks();
initialised = 1;
@@ -530,7 +448,10 @@ handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
if (ntohl(rx_message->data.local.on_off)) {
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
ntohl(rx_message->data.local.orphan));
ntohl(rx_message->data.local.orphan),
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();
}
@@ -720,9 +641,10 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Source_Type type;
SourceParameters params;
int family, pool, port;
NSR_Status status;
uint32_t flags;
char *name;
int pool, port;
switch (ntohl(rx_message->data.ntp_source.type)) {
case REQ_ADDSRC_SERVER:
@@ -750,6 +672,10 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
flags = ntohl(rx_message->data.ntp_source.flags);
family = flags & REQ_ADDSRC_IPV4 ? IPADDR_INET4 :
flags & REQ_ADDSRC_IPV6 ? IPADDR_INET6 : IPADDR_UNSPEC;
port = ntohl(rx_message->data.ntp_source.port);
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
@@ -775,21 +701,19 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields = (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_MONO_ROOT ?
NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
params.connectivity = flags & REQ_ADDSRC_ONLINE ? SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = !!(flags & REQ_ADDSRC_AUTOOFFLINE);
params.iburst = !!(flags & REQ_ADDSRC_IBURST);
params.interleaved = !!(flags & REQ_ADDSRC_INTERLEAVED);
params.burst = !!(flags & REQ_ADDSRC_BURST);
params.nts = !!(flags & REQ_ADDSRC_NTS);
params.copy = !!(flags & REQ_ADDSRC_COPY);
params.ext_fields = (flags & REQ_ADDSRC_EF_EXP_MONO_ROOT ? NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
(flags & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
status = NSR_AddSourceByName(name, family, port, pool, type, &params, NULL);
switch (status) {
case NSR_Success:
break;
@@ -807,6 +731,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->status = htons(STT_INVALIDNAME);
break;
case NSR_InvalidAF:
tx_message->status = htons(STT_INVALIDAF);
break;
case NSR_NoSuchSource:
assert(0);
break;
@@ -1225,7 +1151,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
tx_message->reply = htons(RPY_NTP_DATA);
tx_message->reply = htons(RPY_NTP_DATA2);
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
@@ -1253,6 +1179,10 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
tx_message->data.ntp_data.total_kernel_tx_ts = htonl(report.total_kernel_tx_ts);
tx_message->data.ntp_data.total_kernel_rx_ts = htonl(report.total_kernel_rx_ts);
tx_message->data.ntp_data.total_hw_tx_ts = htonl(report.total_hw_tx_ts);
tx_message->data.ntp_data.total_hw_rx_ts = htonl(report.total_hw_rx_ts);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
@@ -1409,18 +1339,260 @@ handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_offset(CMD_Request *rx_message, CMD_Reply *tx_message)
{
uint32_t ref_id;
IPAddr ip_addr;
double offset;
UTI_IPNetworkToHost(&rx_message->data.modify_offset.address, &ip_addr);
ref_id = ntohl(rx_message->data.modify_offset.ref_id);
offset = UTI_FloatNetworkToHost(rx_message->data.modify_offset.new_offset);
if ((ip_addr.family != IPADDR_UNSPEC && !NSR_ModifyOffset(&ip_addr, offset)) ||
(ip_addr.family == IPADDR_UNSPEC && !RCL_ModifyOffset(ref_id, offset)))
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;
@@ -1433,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) ||
@@ -1483,9 +1650,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* Don't reply to all requests from hosts other than localhost if the rate
is excessive */
if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
if (!localhost && log_index >= 0 &&
CLG_LimitServiceRate(CLG_CMDMON, log_index) != CLG_PASS) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
}
expected_length = PKL_CommandLength(&rx_message);
@@ -1533,293 +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_LOCAL2:
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;
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,13 +39,19 @@
/* ================================================== */
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;
uint32_t ef_type;
int n, sel_option;
src->family = IPADDR_UNSPEC;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
@@ -81,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;
@@ -103,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;
@@ -122,74 +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;
}
/* ================================================== */
@@ -290,38 +300,53 @@ CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
CPS_Status
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate,
double *wait_synced, double *wait_unsynced)
{
int n;
char *cmd;
*stratum = 10;
*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 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,14 +30,22 @@
#include "srcparams.h"
#include "addressing.h"
typedef enum {
CPS_Success,
CPS_InvalidValue,
CPS_InvalidOption,
CPS_MissingArgument,
} CPS_Status;
typedef struct {
char *name;
int family;
int port;
SourceParameters params;
} 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);
@@ -46,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);
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);

453
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
*
* 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,8 +83,10 @@ 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 *burst, int *leak, int *kod);
static void parse_refclock(char *);
static void parse_smoothtime(char *);
static void parse_source(char *line, char *type, int fatal);
@@ -96,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;
@@ -129,6 +137,9 @@ static int enable_local=0;
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 */
@@ -220,6 +231,7 @@ static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 2;
static int ntp_ratelimit_kod = 0;
static int nts_ratelimit_enabled = 0;
static int nts_ratelimit_interval = 6;
static int nts_ratelimit_burst = 8;
@@ -249,13 +261,19 @@ static REF_LeapMode leapsec_mode = REF_LeapModeSystem;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leapsec_tz = NULL;
/* File name of leap seconds list, usually /usr/share/zoneinfo/leap-seconds.list */
static char *leapsec_list = NULL;
/* Name of the user to which will be dropped root privileges. */
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 *) */
@@ -282,19 +300,23 @@ static double hwts_timeout = 0.001;
/* PTP event port (disabled by default) */
static int ptp_port = 0;
/* PTP domain number of NTP-over-PTP messages */
static int ptp_domain = 123;
typedef struct {
NTP_Source_Type type;
int pool;
CPS_NTP_Source params;
NSR_Status status;
uint32_t conf_id;
} NTP_Source;
/* Array of NTP_Source */
static ARR_Instance ntp_sources;
/* Array of (char *) */
static ARR_Instance ntp_source_dirs;
/* Array of uint32_t corresponding to ntp_sources (for sourcedirs reload) */
static ARR_Instance ntp_source_ids;
/* Flag indicating ntp_sources is used for sourcedirs after config load */
static int conf_ntp_sources_added = 0;
/* Array of RefclockParameters */
static ARR_Instance refclock_sources;
@@ -310,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;
@@ -339,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 : "");
}
/* ================================================== */
@@ -387,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));
@@ -394,13 +431,19 @@ CNF_Initialise(int r, int client_only)
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
ntp_source_dirs = ARR_CreateInstance(sizeof (char *));
ntp_source_ids = ARR_CreateInstance(sizeof (uint32_t));
refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination));
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 *));
@@ -454,13 +497,15 @@ CNF_Finalise(void)
ARR_DestroyInstance(init_sources);
ARR_DestroyInstance(ntp_sources);
ARR_DestroyInstance(ntp_source_dirs);
ARR_DestroyInstance(ntp_source_ids);
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);
@@ -471,6 +516,7 @@ CNF_Finalise(void)
Free(hwclock_file);
Free(keys_file);
Free(leapsec_tz);
Free(leapsec_list);
Free(logdir);
Free(bind_ntp_iface);
Free(bind_acq_iface);
@@ -554,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")) {
@@ -582,10 +628,10 @@ 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);
&cmd_ratelimit_burst, &cmd_ratelimit_leak, NULL);
} else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "confdir")) {
@@ -595,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")) {
@@ -620,14 +666,16 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_leapsecmode(p);
} else if (!strcasecmp(command, "leapsectz")) {
parse_string(p, &leapsec_tz);
} else if (!strcasecmp(command, "leapseclist")) {
parse_string(p, &leapsec_list);
} 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")) {
@@ -637,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")) {
@@ -649,64 +697,70 @@ 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);
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
} else if (!strcasecmp(command, "ntscachedir") ||
!strcasecmp(command, "ntsdumpdir")) {
parse_string(p, &nts_dump_dir);
} 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")) {
parse_string(p, &pidfile);
parse_pidfile(p);
} 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, 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_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")) {
@@ -716,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")) {
@@ -739,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;
@@ -747,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);
}
}
/* ================================================== */
@@ -796,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) {
@@ -816,13 +893,20 @@ 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;
}
source.params.name = Strdup(source.params.name);
source.status = NSR_NoSuchSource;
source.conf_id = 0;
ARR_AppendElement(ntp_sources, &source);
}
@@ -840,7 +924,7 @@ parse_sourcedir(char *line)
/* ================================================== */
static void
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak, int *kod)
{
int n, val;
char *opt;
@@ -850,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;
}
@@ -861,6 +945,8 @@ parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
*burst = val;
else if (!strcasecmp(opt, "leak"))
*leak = val;
else if (!strcasecmp(opt, "kod") && kod)
*kod = val;
else
command_parse_error();
}
@@ -927,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)
@@ -969,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;
@@ -982,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;
}
@@ -1058,8 +1140,16 @@ parse_log(char *line)
static void
parse_local(char *line)
{
if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance))
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;
}
@@ -1220,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)
{
@@ -1461,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)
@@ -1512,6 +1668,20 @@ parse_hwtimestamp(char *line)
/* ================================================== */
static void
parse_pidfile(char *line)
{
parse_string(line, &pidfile);
/* / disables the PID file handling */
if (strcmp(pidfile, "/") == 0) {
Free(pidfile);
pidfile = NULL;
}
}
/* ================================================== */
static const char *
get_basename(const char *path)
{
@@ -1592,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)
{
@@ -1664,6 +1857,8 @@ compare_sources(const void *a, const void *b)
return d;
if ((d = (int)sa->pool - (int)sb->pool) != 0)
return d;
if ((d = (int)sa->params.family - (int)sb->params.family) != 0)
return d;
if ((d = (int)sa->params.port - (int)sb->params.port) != 0)
return d;
return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params));
@@ -1676,18 +1871,17 @@ reload_source_dirs(void)
{
NTP_Source *prev_sources, *new_sources, *source;
unsigned int i, j, prev_size, new_size, unresolved;
uint32_t *prev_ids, *new_ids;
char buf[MAX_LINE_LENGTH];
NSR_Status s;
int d, pass;
prev_size = ARR_GetSize(ntp_source_ids);
if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size)
assert(0);
/* Ignore reload command before adding configured sources */
if (!conf_ntp_sources_added)
return;
/* Save the current sources and their configuration IDs */
prev_ids = MallocArray(uint32_t, prev_size);
memcpy(prev_ids, ARR_GetElements(ntp_source_ids), prev_size * sizeof (prev_ids[0]));
prev_size = ARR_GetSize(ntp_sources);
/* Save the current sources */
prev_sources = MallocArray(NTP_Source, prev_size);
memcpy(prev_sources, ARR_GetElements(ntp_sources), prev_size * sizeof (prev_sources[0]));
@@ -1705,8 +1899,6 @@ reload_source_dirs(void)
new_size = ARR_GetSize(ntp_sources);
new_sources = ARR_GetElements(ntp_sources);
ARR_SetSize(ntp_source_ids, new_size);
new_ids = ARR_GetElements(ntp_source_ids);
unresolved = 0;
LOG_SetContext(LOGC_SourceFile);
@@ -1721,30 +1913,31 @@ reload_source_dirs(void)
d = i < prev_size ? -1 : 1;
/* Remove missing sources before adding others to avoid conflicts */
if (pass == 0 && d < 0 && prev_sources[i].params.name[0] != '\0') {
NSR_RemoveSourcesById(prev_ids[i]);
if (pass == 0 && d < 0 && prev_sources[i].status == NSR_Success) {
NSR_RemoveSourcesById(prev_sources[i].conf_id);
}
/* Add new sources */
if (pass == 1 && d > 0) {
/* Add new sources and sources that could not be added before */
if (pass == 1 && (d > 0 || (d == 0 && prev_sources[i].status != NSR_Success))) {
source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, &new_ids[j]);
s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
source->pool, source->type, &source->params.params,
&source->conf_id);
source->status = s;
if (s == NSR_UnresolvedName) {
unresolved++;
} else if (s != NSR_Success) {
} else if (s != NSR_Success && (d > 0 || s != prev_sources[i].status)) {
LOG(LOGS_ERR, "Could not add source %s : %s",
source->params.name, NSR_StatusToString(s));
/* Mark the source as not present */
source->params.name[0] = '\0';
}
}
/* Keep unchanged sources */
if (pass == 1 && d == 0)
new_ids[j] = prev_ids[i];
if (pass == 1 && d == 0) {
new_sources[j].status = prev_sources[i].status;
new_sources[j].conf_id = prev_sources[i].conf_id;
}
}
}
@@ -1753,7 +1946,6 @@ reload_source_dirs(void)
for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name);
Free(prev_sources);
Free(prev_ids);
if (unresolved > 0)
NSR_ResolveSources();
@@ -1842,15 +2034,17 @@ CNF_AddSources(void)
for (i = 0; i < ARR_GetSize(ntp_sources); i++) {
source = (NTP_Source *)ARR_GetElement(ntp_sources, i);
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, NULL);
s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
source->pool, source->type, &source->params.params, NULL);
if (s != NSR_Success && s != NSR_UnresolvedName)
LOG(LOGS_ERR, "Could not add source %s", source->params.name);
Free(source->params.name);
}
/* The arrays will be used for sourcedir (re)loading */
ARR_SetSize(ntp_sources, 0);
conf_ntp_sources_added = 1;
reload_source_dirs();
}
@@ -1916,8 +2110,9 @@ CNF_GetAcquisitionPort(void)
/* ================================================== */
char *
CNF_GetDriftFile(void)
CNF_GetDriftFile(int *interval)
{
*interval = drift_file_interval;
return drift_file;
}
@@ -2140,6 +2335,14 @@ CNF_GetManualEnabled(void)
/* ================================================== */
ARR_Instance
CNF_GetOpenCommands(void)
{
return open_commands;
}
/* ================================================== */
int
CNF_GetCommandPort(void) {
return cmd_port;
@@ -2148,12 +2351,16 @@ CNF_GetCommandPort(void) {
/* ================================================== */
int
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance)
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;
@@ -2386,6 +2593,14 @@ CNF_GetLeapSecTimezone(void)
/* ================================================== */
char *
CNF_GetLeapSecList(void)
{
return leapsec_list;
}
/* ================================================== */
int
CNF_GetSchedPriority(void)
{
@@ -2402,11 +2617,12 @@ CNF_GetLockMemory(void)
/* ================================================== */
int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak, int *kod)
{
*interval = ntp_ratelimit_interval;
*burst = ntp_ratelimit_burst;
*leak = ntp_ratelimit_leak;
*kod = ntp_ratelimit_kod;
return ntp_ratelimit_enabled;
}
@@ -2540,6 +2756,14 @@ CNF_GetPtpPort(void)
/* ================================================== */
int
CNF_GetPtpDomain(void)
{
return ptp_domain;
}
/* ================================================== */
int
CNF_GetRefresh(void)
{
@@ -2548,6 +2772,14 @@ CNF_GetRefresh(void)
/* ================================================== */
ARR_Instance
CNF_GetNtsAeads(void)
{
return nts_aeads;
}
/* ================================================== */
char *
CNF_GetNtsDumpDir(void)
{
@@ -2624,8 +2856,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);
}

12
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);
@@ -91,6 +93,7 @@ extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
extern char *CNF_GetLeapSecList(void);
/* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void);
@@ -107,14 +110,15 @@ 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);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
double *wait_synced, double *wait_unsynced);
extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak, int *kod);
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
@@ -158,9 +162,11 @@ extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void);
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,7 @@
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2023
// Copyright (C) Miroslav Lichvar 2009-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
@@ -69,12 +69,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 +115,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
@@ -126,6 +133,15 @@ mechanism. Unlike with the *key* option, the server and client do not need to
share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using
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
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).
+
The NTS-KE server can be specified by IP address if it is included in the
server's certificate as a Subject Alternative Name (SAN).
*certset* _ID_:::
This option specifies which set of trusted certificates should be used to verify
the server's certificate when the *nts* option is enabled. Sets of certificates
@@ -136,10 +152,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
@@ -149,7 +165,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
@@ -189,7 +205,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
@@ -220,7 +236,7 @@ when disconnecting the network link. (It will still be necessary to use the
<<chronyc.adoc#online,*online*>> command when the link has been established, to
enable measurements to start.)
*prefer*:::
Prefer this source over sources without the *prefer* option.
Prefer this source over other selectable sources without the *prefer* option.
*noselect*:::
Never select this source. This is particularly useful for monitoring.
*trust*:::
@@ -283,7 +299,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.
+
@@ -294,7 +310,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
@@ -302,7 +318,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*:::
@@ -343,6 +359,12 @@ the PTP port. The corrections are applied only to NTP measurements with HW
timestamps (enabled by the <<hwtimestamp,*hwtimestamp*>> directive). This
field should be enabled only for servers known to be running *chronyd* version
4.5 or later.
*ipv4*:::
*ipv6*:::
These options force *chronyd* to use only IPv4 or IPv6 addresses respectively
for this source. They do not override the *-4* or *-6* option on the *chronyd*
command line.
{blank}:::
[[pool]]*pool* _name_ [_option_]...::
@@ -359,7 +381,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
@@ -383,7 +405,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.
@@ -411,7 +433,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.
@@ -440,7 +462,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.
@@ -463,7 +485,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
@@ -493,7 +515,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
@@ -542,8 +564,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
@@ -581,6 +604,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:
+
@@ -607,18 +647,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*:::
@@ -632,7 +672,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).
@@ -646,16 +686,15 @@ 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 sources without the prefer option.
Prefer this source over other selectable sources without the *prefer* option.
*noselect*:::
Never select this source. This is useful for monitoring or with sources which
are not very accurate, but are locked with a PPS refclock.
@@ -674,9 +713,10 @@ trusted and required source.
*tai*:::
This option indicates that the reference clock keeps time in TAI instead of UTC
and that *chronyd* should correct its offset by the current TAI-UTC offset. The
<<leapsectz,*leapsectz*>> directive must be used with this option and the
database must be kept up to date in order for this correction to work as
expected. This option does not make sense with PPS refclocks.
<<leapsectz,*leapsectz*>> or <<leapseclist,*leapseclist*>> directive must be
used with this option and the database must be kept up to date in order for
this correction to work as expected. This option does not make sense with PPS
refclocks.
*local*:::
This option specifies that the reference clock is an unsynchronised clock which
is more stable than the system clock (e.g. TCXO, OCXO, or atomic clock) and
@@ -817,6 +857,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
@@ -845,7 +913,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.
+
@@ -880,7 +948,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
@@ -894,17 +962,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.
@@ -917,11 +986,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
@@ -997,7 +1066,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_::
@@ -1026,14 +1095,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.
@@ -1053,11 +1122,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'
@@ -1080,7 +1149,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
@@ -1093,7 +1162,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.
+
@@ -1112,6 +1181,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:
+
----
@@ -1135,7 +1208,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`'.
@@ -1194,7 +1267,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
@@ -1227,15 +1300,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.
@@ -1263,8 +1336,21 @@ $ TZ=right/UTC date -d 'Dec 31 2008 23:59:60'
Wed Dec 31 23:59:60 UTC 2008
----
[[leapseclist]]*leapseclist* _file_::
This directive specifies the path to a file containing a list of leap seconds
and TAI-UTC offsets in NIST/IERS format. It is recommended to use
the file _leap-seconds.list_ usually included with the system timezone
database. The behaviour of this directive is otherwise equivalent to
<<leapsectz,*leapsectz*>>.
+
An example of this directive is:
+
----
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
@@ -1286,6 +1372,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
@@ -1493,13 +1582,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
@@ -1526,7 +1615,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.
@@ -1544,7 +1633,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_::
@@ -1638,16 +1727,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:
@@ -1655,6 +1747,14 @@ The current root distance can be calculated from root delay and root dispersion
----
distance = delay / 2 + dispersion
----
*activate* _distance_:::
This option sets an activating root distance for the local reference. The
local reference will not be used until the root distance drops below the
configured value for the first time. This can be used to prevent the local
reference from being activated on a server which has never been synchronised
with an upstream server. The default value of 0.0 causes no activating
distance to be used, such that the local reference is always eligible for
activation.
*orphan*:::
This option enables a special '`orphan`' mode, where sources with stratum equal
to the local _stratum_ are assumed to not serve real time. They are ignored
@@ -1662,7 +1762,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
@@ -1672,12 +1772,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 10 orphan distance 0.1
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_::
@@ -1726,7 +1844,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_::
@@ -1734,7 +1852,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
@@ -1742,6 +1860,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
@@ -1759,9 +1914,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.
@@ -1813,7 +1968,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.
+
@@ -1831,7 +1986,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
@@ -1839,7 +1994,15 @@ 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
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
maximum value is 4.
{blank}::
+
@@ -1856,7 +2019,7 @@ packets, by up to 75% (with default *leak* of 2).
[[ntsratelimit]]*ntsratelimit* [_option_]...::
This directive enables rate limiting of NTS-KE requests. It is similar to the
<<ratelimit,*ratelimit*>> directive, except the default interval is 6
(1 connection per 64 seconds).
(1 connection per 64 seconds) and the *kod* option is not supported.
+
An example of the use of the directive is:
+
@@ -1868,13 +2031,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
@@ -1888,7 +2051,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.
@@ -1972,6 +2135,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.
@@ -2004,8 +2170,8 @@ need to be run with the *-p 257* option to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is
similar to the <<ratelimit,*ratelimit*>> directive, except responses to
localhost are never limited and the default interval is -4 (16 packets per
second).
localhost are never limited, the default interval is -4 (16 packets per
second), and the *kod* option is not supported.
+
An example of the use of the directive is:
+
@@ -2013,6 +2179,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_::
@@ -2078,7 +2265,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
@@ -2093,7 +2280,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
@@ -2143,8 +2331,8 @@ from the example line above):
. Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
*maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
0=fail). The first test from these four also checks the server precision,
response time, and whether an interleaved response is acceptable for
synchronisation. [1111]
response time, validity of the measured offset, and whether an interleaved
response is acceptable for synchronisation. [1111]
. Local poll [10]
. Remote poll [10]
. '`Score`' (an internal score within each polling level used to decide when to
@@ -2166,7 +2354,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.
+
@@ -2207,7 +2395,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.
@@ -2426,9 +2614,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
@@ -2518,7 +2706,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.
@@ -2604,8 +2792,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:
@@ -2645,7 +2833,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.
@@ -2660,7 +2848,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.
+
@@ -2687,7 +2875,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
@@ -2711,7 +2899,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
@@ -2736,11 +2928,13 @@ e.g.:
----
pidfile /run/chronyd.pid
----
+
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
@@ -2753,7 +2947,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.
@@ -2766,6 +2960,11 @@ hwtimestamp * rxfilter ptp
ptpport 319
----
[[ptpdomain]]*ptpdomain* _domain_::
The *ptpdomain* directive sets the PTP domain number of transmitted and
accepted NTP-over-PTP messages. Messages from other domains are ignored.
The default is 123, the minimum is 0, and the maximum is 255.
[[sched_priority]]*sched_priority* _priority_::
On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
select the SCHED_FIFO real-time scheduler at the specified priority (which must
@@ -2805,7 +3004,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
@@ -2831,7 +3030,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
@@ -2920,7 +3119,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
@@ -2935,7 +3134,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.
@@ -3003,7 +3202,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
@@ -3014,7 +3213,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.
@@ -3057,7 +3256,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
@@ -459,8 +465,8 @@ states are reported.
The following states indicate the source is not considered selectable for
synchronisation:
* _N_ - has the *noselect* option.
* _s_ - is not synchronised.
* _M_ - does not have enough measurements.
* _s_ - is not synchronised.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
@@ -492,7 +498,7 @@ local clock:
This column shows the name or IP address of the source if it is an NTP server,
or the reference ID if it is a reference clock.
*Auth*:::
This column indicites whether an authentication mechanism is enabled for the
This column indicates whether an authentication mechanism is enabled for the
source. _Y_ means yes and _N_ means no.
*COpts*:::
This column displays the configured selection options of the source.
@@ -556,6 +562,13 @@ The *reselectdist* command sets the reselection distance. It is equivalent to
the <<chrony.conf.adoc#reselectdist,*reselectdist*>> directive in the
configuration file.
[[offset]]*offset* _address|refid_ _offset_::
The *offset* command modifies the offset correction of an NTP source specified
by IP address (or the _ID#XXXXXXXXXX_ identifier used for unknown addresses),
or a reference clock specified by reference ID as a string. It is equivalent to
the *offset* option in the <<chrony.conf.adoc#server,*server*>> or
<<chrony.conf.adoc#refclock,*refclock*>> directive respectively.
=== NTP sources
[[activity]]*activity*::
@@ -689,6 +702,10 @@ Total TX : 24
Total RX : 24
Total valid RX : 24
Total good RX : 22
Total kernel TX : 24
Total kernel RX : 24
Total HW TX : 0
Total HW RX : 0
----
+
The fields are explained as follows:
@@ -746,6 +763,18 @@ The number of packets which passed the first two groups of NTP tests.
*Total good RX*:::
The number of packets which passed all three groups of NTP tests, i.e. the NTP
measurement was accepted.
*Total kernel TX*:::
The number of packets sent to the source for which a timestamp was captured by
the kernel.
*Total kernel RX*:::
The number of packets received from the source for which a timestamp was
captured by the kernel.
*Total HW TX*:::
The number of packets sent to the source for which a timestamp was captured by
the NIC.
*Total HW RX*:::
The number of packets received from the source for which a timestamp was
captured by the NIC.
[[add_peer]]*add peer* _name_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
@@ -1438,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 containg 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_

74
doc/contributing.adoc Normal file
View File

@@ -0,0 +1,74 @@
// This file is part of chrony
//
// Copyright (C) Miroslav Lichvar 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.
= Contributing
== Patches
The source code of `chrony` is maintained in a git repository at
https://gitlab.com/chrony/chrony. Patches can be submitted to the `chrony-dev`
mailing list, or as a merge request on gitlab. Before spending a lot of time
implementing a new major feature, it is recommended to ask on the mailing list
for comments about its design and whether such feature fits the goals of the
project.
Each commit should be a self-contained logical change, which does not break
the build or tests. New functionality and fixed bugs should be covered by a new
test or an extended existing test in the test suite. The test can be included
in the same commit or added as a separate commit. The same rule applies to
documentation. All command-line options, configuration directives, and
`chronyc` commands should be documented.
The most important tests can be executed by running `make check` or `make
quickcheck`. The unit and system tests run on all supported systems. The system
tests require root privileges. The simulation tests run only on Linux and
require https://gitlab.com/chrony/clknetsim[clknetsim] to be compiled in the
directory containing the tests, but they are executed with a merge request on
gitlab.
The commit message should explain any non-trivial changes, e.g. what problem is
the commit solving and how. The commit subject (first line of the message)
should be written in an imperative form, prefixed with the component name if it
is not a more general change, starting in lower case, and no period at the end.
See the git log for examples.
Simpler code is better. Less code is better. Security is a top priority.
Assertions should catch only bugs in the `chrony` code. Unexpected values in
external input (e.g. anything received from network) must be handled correctly
without crashing and memory corruption. Fuzzing support is available at
https://gitlab.com/chrony/chrony-fuzz. The fuzzing coverage is checked by the
project maintainer before each release.
The code should mostly be self-documenting. Comments should explain the
less obvious things.
== Coding style
The code uses two spaces for indentation. No tabs. The line length should
normally not exceed 95 characters. Too much indentation indicates the code will
not be very readable.
Function names are in an imperative form. Names of static functions use
lowercase characters and underscores. Public functions, structures, typedefs
are in CamelCase with a prefix specific to the module (e.g. LCL - local, NCR
- NTP core, NKS - NTS-KE server, SST - sourcestats).
Function names are not followed by space, but keywords of the language (e.g.
`if`, `for`, `while`, `sizeof`) are followed by space.
Have a look at the existing code to get a better idea what is expected.

View File

@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Luke Valenta 2023
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2023
// Copyright (C) Miroslav Lichvar 2014-2016, 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
@@ -772,6 +772,17 @@ print all sources, even those that do not have a known address yet, with their
names as they were specified in the configuration. This can be useful to verify
that the names specified in the configuration are used as expected.
When DNSSEC is enabled, it will not work until the time is synchronized, as it
requires validating a signature timestamp and its expiration date, so if the
system time is too far in the future or the past DNSSEC validation will fail and
`chronyd` will be unable to resolve the address of the NTP server. In such cases,
if hostnames are the only options and bare IP addresses cannot be used, DNSSEC
can be disabled for `chronyd` using resolver-specific mechanisms, if available,
although of course that means losing the protection afforded by DNSSEC.
For example, when using systemd-resolved, the `SYSTEMD_NSS_RESOLVE_VALIDATE=0`
environment variable can be set, for example in the `chronyd` systemd unit via
`Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0`.
=== Is `chronyd` allowed to step the system clock?
By default, `chronyd` adjusts the clock gradually by slowing it down or
@@ -1155,6 +1166,53 @@ There are several different clocks used by `chronyd`:
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
order to convert the hardware timestamps.
=== How accurate is my system clock?
`chronyd` does not know how accurate really is the clock it is synchronizing.
Even if the measured offset of the clock is stable to nanoseconds, it could be
off by milliseconds due to asymmetric network delay, e.g. caused by asymmetric
routing or queuing delays in network switches. NTP provides root delay and root
dispersion to enable clients to estimate the maximum error of their clock.
Root delay measures the sum of round-trip times between all NTP servers on the
path from the client to the primary time source (e.g. a GPS receiver). Half of
the root delay is the maximum error due to asymmetric delays, assuming one
direction (e.g. from the client to the server) has a zero delay and the other
direction (from the server to the client) takes all of the measured delay. The
root delay also covers timestamping errors if the server implementation and
hardware meet the NTP requirement for transmit timestamps to never be late and
receive timestamps to never be early.
If you have additional information about the hardware and network between the
client and primary time source, you could modify the root delay to get a better
estimate of the maximum error. For example, from the physical distance of the
server and signal propagation speed in the cables a minimum symmetric
round-trip delay can be calculated and subtracted from the root delay measured
by NTP.
Root dispersion estimates errors due to instability of clocks and NTP
measurements. `chronyd` adjusts the rate at which root dispersion grows between
updates of the clock according to the stability of its NTP measurements. The
minimum rate is set by the the `maxclockerror` directive. By default it is 1
ppm (1 microsecond per second).
The estimated maximum error of the NTP clock is the sum of the root dispersion
and half of the root delay. This value is called root distance. The current
values of root dispersion and delay are included in the `tracking` report.
The estimated maximum error of the system clock, which is synchronized to the
NTP clock, is the sum of the root distance and remaining correction of the
system clock provided as `System time` in the `tracking` report. A maximum
value of this estimate between updates of the clock is included in the
`tracking` log.
Note that the resolution of the root delay and root dispersion fields in NTP
messages is about 15 microseconds and `chronyd` rounds the values up, i.e. the
minimum root distance an NTP client can normally observe is about 22.5
microseconds. An NTP extension field containing root delay and dispersion in a
better resolution of about 4 nanoseconds can be enabled by the `extfield F323`
option.
== Operating systems
=== Does `chrony` support Windows?

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

@@ -37,8 +37,8 @@ ntsdumpdir /var/lib/chrony
# Insert/delete leap seconds by slewing instead of stepping.
#leapsecmode slew
# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC
# Set the TAI-UTC offset of the system clock.
#leapseclist /usr/share/zoneinfo/leap-seconds.list
# Specify directory for log files.
logdir /var/log/chrony

View File

@@ -126,11 +126,11 @@ ntsdumpdir /var/lib/chrony
! pidfile /var/run/chrony/chronyd.pid
# If the system timezone database is kept up to date and includes the
# right/UTC timezone, chronyd can use it to determine the current
# TAI-UTC offset and when will the next leap second occur.
# The system timezone database usually comes with a list of leap seconds and
# corresponding TAI-UTC offsets. chronyd can use it to set the offset of the
# system TAI clock and have an additional source of leap seconds.
! leapsectz right/UTC
! leapseclist /usr/share/zoneinfo/leap-seconds.list
#######################################################################
### INITIAL CLOCK CORRECTION

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;

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);
}
/* ================================================== */

272
leapdb.c Normal file
View File

@@ -0,0 +1,272 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
* Copyright (C) Patrick Oppenlander 2023, 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.
*
**********************************************************************
=======================================================================
This module provides leap second information. */
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "leapdb.h"
#include "logging.h"
#include "util.h"
/* ================================================== */
/* Source of leap second data */
enum {
SRC_NONE,
SRC_TIMEZONE,
SRC_LIST,
} leap_src;
/* Offset between leap-seconds.list timestamp epoch and Unix epoch.
leap-seconds.list epoch is 1 Jan 1900, 00:00:00 */
#define LEAP_SEC_LIST_OFFSET 2208988800
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when, int *tai_offset)
{
struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
NTP_Leap tz_leap = LEAP_Normal;
tm = gmtime(&when);
if (!tm)
return tz_leap;
stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
if (tz_env) {
if (strlen(tz_env) >= sizeof (tz_orig))
return tz_leap;
strcpy(tz_orig, tz_env);
}
setenv("TZ", CNF_GetLeapSecTimezone(), 1);
tzset();
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
t = mktime(&stm);
if (t != -1)
*tai_offset = t - when + 10;
/* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60;
stm.tm_min = 59;
stm.tm_hour = 23;
t = mktime(&stm);
if (tz_env)
setenv("TZ", tz_orig, 1);
else
unsetenv("TZ");
tzset();
if (t == -1)
return tz_leap;
if (stm.tm_sec == 60)
tz_leap = LEAP_InsertSecond;
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
return tz_leap;
}
/* ================================================== */
static NTP_Leap
get_list_leap(time_t when, int *tai_offset)
{
FILE *f;
char line[1024];
NTP_Leap ret_leap = LEAP_Normal;
int ret_tai_offset = 0, prev_lsl_tai_offset = 10;
int64_t when1900, lsl_updated = 0, lsl_expiry = 0;
const char *leap_sec_list = CNF_GetLeapSecList();
if (!(f = UTI_OpenFile(NULL, leap_sec_list, NULL, 'r', 0))) {
LOG(LOGS_ERR, "Failed to open leap seconds list %s", leap_sec_list);
goto out;
}
/* Leap second happens at midnight */
when = (when / (24 * 3600) + 1) * (24 * 3600);
/* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
while (fgets(line, sizeof line, f) > 0) {
int64_t lsl_when;
int lsl_tai_offset;
char *p;
/* Ignore blank lines */
for (p = line; *p && isspace(*p); ++p)
;
if (!*p)
continue;
if (*line == '#') {
/* Update time line starts with #$ */
if (line[1] == '$' && sscanf(line + 2, "%"SCNd64, &lsl_updated) != 1)
goto error;
/* Expiration time line starts with #@ */
if (line[1] == '@' && sscanf(line + 2, "%"SCNd64, &lsl_expiry) != 1)
goto error;
/* Comment or a special comment we don't care about */
continue;
}
/* Leap entry */
if (sscanf(line, "%"SCNd64" %d", &lsl_when, &lsl_tai_offset) != 2)
goto error;
if (when1900 == lsl_when) {
if (lsl_tai_offset > prev_lsl_tai_offset)
ret_leap = LEAP_InsertSecond;
else if (lsl_tai_offset < prev_lsl_tai_offset)
ret_leap = LEAP_DeleteSecond;
/* When is rounded to the end of the day, so offset hasn't changed yet! */
ret_tai_offset = prev_lsl_tai_offset;
} else if (when1900 > lsl_when) {
ret_tai_offset = lsl_tai_offset;
}
prev_lsl_tai_offset = lsl_tai_offset;
}
/* Make sure the file looks sensible */
if (!feof(f) || !lsl_updated || !lsl_expiry)
goto error;
if (when1900 >= lsl_expiry)
LOG(LOGS_WARN, "Leap second list %s needs update", leap_sec_list);
goto out;
error:
if (f)
fclose(f);
LOG(LOGS_ERR, "Failed to parse leap seconds list %s", leap_sec_list);
return LEAP_Normal;
out:
if (f)
fclose(f);
*tai_offset = ret_tai_offset;
return ret_leap;
}
/* ================================================== */
static int
check_leap_source(NTP_Leap (*src)(time_t when, int *tai_offset))
{
int tai_offset = 0;
/* Check that the leap second source has good data for Jun 30 2012 and Dec 31 2012 */
if (src(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
src(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35)
return 1;
return 0;
}
/* ================================================== */
void
LDB_Initialise(void)
{
const char *leap_tzname, *leap_sec_list;
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname && !check_leap_source(get_tz_leap)) {
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
leap_sec_list = CNF_GetLeapSecList();
if (leap_sec_list && !check_leap_source(get_list_leap)) {
LOG(LOGS_WARN, "Leap second list %s failed check, ignoring", leap_sec_list);
leap_sec_list = NULL;
}
if (leap_sec_list) {
LOG(LOGS_INFO, "Using leap second list %s", leap_sec_list);
leap_src = SRC_LIST;
} else if (leap_tzname) {
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
leap_src = SRC_TIMEZONE;
}
}
/* ================================================== */
NTP_Leap
LDB_GetLeap(time_t when, int *tai_offset)
{
static time_t last_ldb_leap_check;
static NTP_Leap ldb_leap;
static int ldb_tai_offset;
/* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600);
if (last_ldb_leap_check == when)
goto out;
last_ldb_leap_check = when;
ldb_leap = LEAP_Normal;
ldb_tai_offset = 0;
switch (leap_src) {
case SRC_NONE:
break;
case SRC_TIMEZONE:
ldb_leap = get_tz_leap(when, &ldb_tai_offset);
break;
case SRC_LIST:
ldb_leap = get_list_leap(when, &ldb_tai_offset);
break;
}
out:
*tai_offset = ldb_tai_offset;
return ldb_leap;
}
/* ================================================== */
void
LDB_Finalise(void)
{
/* Nothing to do */
}

37
leapdb.h Normal file
View File

@@ -0,0 +1,37 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Patrick Oppenlander 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.
*
**********************************************************************
=======================================================================
This module provides leap second information.
*/
#ifndef GOT_LEAPDB_H
#define GOT_LEAPDB_H
#include "ntp.h"
extern void LDB_Initialise(void);
extern NTP_Leap LDB_GetLeap(time_t when, int *tai_offset);
extern void LDB_Finalise(void);
#endif /* GOT_LEAPDB_H */

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;
@@ -92,6 +93,9 @@ LOG_Finalise(void)
if (file_log)
fclose(file_log);
file_log = NULL;
Free(file_log_path);
file_log_path = NULL;
LOG_CycleLogFiles();
@@ -185,7 +189,7 @@ void LOG_Message(LOG_Severity severity,
/* Send the message also to the foreground process if it is
still running, or stderr if it is still open */
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
if (!LOG_NotifyParent(buf))
; /* Not much we can do here */
} else if (system_log && parent_fd == 0) {
system_log = 0;
@@ -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
@@ -291,6 +310,17 @@ LOG_SetParentFd(int fd)
/* ================================================== */
int
LOG_NotifyParent(const char *message)
{
if (parent_fd <= 0)
return 1;
return write(parent_fd, message, strlen(message) + 1) > 0;
}
/* ================================================== */
void
LOG_CloseParentFd()
{
@@ -374,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;
}
}
}
/* ================================================== */

View File

@@ -126,7 +126,10 @@ extern void LOG_OpenSystemLog(void);
/* Stop using stderr and send fatal message to the foreground process */
extern void LOG_SetParentFd(int fd);
/* Close the pipe to the foreground process so it can exit */
/* Send a message to the foreground process */
extern int LOG_NotifyParent(const char *message);
/* Close the pipe to the foreground process */
extern void LOG_CloseParentFd(void);
/* File logging functions */

51
main.c
View File

@@ -32,6 +32,7 @@
#include "main.h"
#include "sched.h"
#include "leapdb.h"
#include "local.h"
#include "sys.h"
#include "ntp_io.h"
@@ -106,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");
SCK_CloseSocket(sock_fd);
#endif
}
/* ================================================== */
void
MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
notify_system_manager(0);
LCL_CancelOffsetCorrection();
SRC_DumpSources();
@@ -134,6 +164,7 @@ MAI_CleanupAndExit(void)
RCL_Finalise();
SRC_Finalise();
REF_Finalise();
LDB_Finalise();
RTC_Finalise();
SYS_Finalise();
@@ -213,7 +244,12 @@ post_init_ntp_hook(void *anything)
REF_SetMode(ref_mode);
}
/* Close the pipe to the foreground process so it can exit */
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(""))
SCH_QuitProgram();
LOG_CloseParentFd();
CNF_AddSources();
@@ -334,10 +370,13 @@ go_daemon(void)
/* Don't exit before the 'parent' */
waitpid(pid, NULL, 0);
close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message));
if (r) {
if (r > 0) {
close(pipefd[0]);
close(pipefd[1]);
if (r != 1 || message[0] != '\0') {
if (r > 1) {
/* Print the error message from the child */
message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
@@ -346,8 +385,6 @@ go_daemon(void)
} else
exit(0);
} else {
close(pipefd[0]);
setsid();
/* Do 2nd fork, as-per recommended practice for launching daemons. */
@@ -357,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 {
@@ -655,6 +693,7 @@ int main
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
LDB_Initialise();
REF_Initialise();
SST_Initialise();
NSR_Initialise();

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2023
* Copyright (C) Miroslav Lichvar 2009-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
@@ -221,7 +221,7 @@ struct NCR_Instance_Record {
int burst_good_samples_to_go;
int burst_total_samples_to_go;
/* Report from last valid response */
/* Report from last valid response and packet/timestamp statistics */
RPT_NTPReport report;
};
@@ -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);
@@ -2211,7 +2213,8 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
/* Test A combines multiple tests to avoid changing the measurements log
format and ntpdata report. It requires that the minimum estimate of the
peer delay is not larger than the configured maximum, it is not a
response in the 'warm up' exchange, in both client modes that the server
response in the 'warm up' exchange, the configured offset correction is
within the supported NTP interval, both client modes that the server
processing time is sane, in interleaved client/server mode that the
previous response was not in basic mode (which prevents using timestamps
that minimise delay error), and in interleaved symmetric mode that the
@@ -2220,6 +2223,7 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
precision <= inst->max_delay &&
inst->presend_done <= 0 &&
UTI_IsTimeOffsetSane(&sample.time, sample.offset) &&
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_CLIENT && interleaved_packet &&
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
@@ -2333,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,
@@ -2372,13 +2375,17 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
SRC_UpdateReachability(inst->source, synced_packet);
if (synced_packet) {
if (inst->copy && inst->remote_stratum > 0) {
/* Assume the reference ID and stratum of the server */
if (inst->copy) {
/* Assume the reference ID and stratum of the server */
if (synced_packet && inst->remote_stratum > 0) {
inst->remote_stratum--;
SRC_SetRefid(inst->source, ntohl(message->reference_id), &inst->remote_addr.ip_addr);
} else {
SRC_ResetInstance(inst->source);
}
}
if (synced_packet) {
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
if (inst->delay_quant)
@@ -2530,6 +2537,10 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_PacketInfo info;
inst->report.total_rx_count++;
if (rx_ts->source == NTP_TS_KERNEL)
inst->report.total_kernel_rx_ts++;
else if (rx_ts->source == NTP_TS_HARDWARE)
inst->report.total_hw_rx_ts++;
if (!parse_packet(message, length, &info))
return 0;
@@ -2652,6 +2663,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
NTP_Local_Timestamp local_tx, *tx_ts;
NTP_int64 ntp_rx, *local_ntp_rx;
int log_index, interleaved, poll, version;
CLG_Limit limit;
uint32_t kod;
/* Ignore the packet if it wasn't received by server socket */
@@ -2697,7 +2709,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
log_index = CLG_LogServiceAccess(CLG_NTP, &remote_addr->ip_addr, &rx_ts->ts);
/* Don't reply to all requests if the rate is excessive */
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTP, log_index)) {
limit = log_index >= 0 ? CLG_LimitServiceRate(CLG_NTP, log_index) : CLG_PASS;
if (limit == CLG_DROP) {
DEBUG_LOG("NTP packet discarded to limit response rate");
return;
}
@@ -2711,6 +2724,13 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return;
}
if (limit == CLG_KOD) {
/* Don't respond if there is a conflict with the NTS NAK */
if (kod != 0)
return;
kod = KOD_RATE;
}
local_ntp_rx = NULL;
tx_ts = NULL;
interleaved = 0;
@@ -2736,7 +2756,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
CLG_DisableNtpTimestamps(&ntp_rx);
}
CLG_UpdateNtpStats(kod != 0 && info.auth.mode != NTP_AUTH_NONE &&
CLG_UpdateNtpStats(kod == 0 && info.auth.mode != NTP_AUTH_NONE &&
info.auth.mode != NTP_AUTH_MSSNTP,
rx_ts->source, interleaved ? tx_ts->source : NTP_TS_DAEMON);
@@ -2812,8 +2832,11 @@ NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
message);
if (tx_ts->source == NTP_TS_HARDWARE) {
inst->report.total_hw_tx_ts++;
if (has_saved_response(inst))
process_saved_response(inst);
} else if (tx_ts->source == NTP_TS_KERNEL) {
inst->report.total_kernel_tx_ts++;
}
}
@@ -3017,6 +3040,16 @@ NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum)
/* ================================================== */
void
NCR_ModifyOffset(NCR_Instance inst, double new_offset)
{
inst->offset_correction = new_offset;
LOG(LOGS_INFO, "Source %s new offset %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_offset);
}
/* ================================================== */
void
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target)
{

View File

@@ -113,6 +113,8 @@ extern void NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_d
extern void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum);
extern void NCR_ModifyOffset(NCR_Instance inst, double new_offset);
extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);

View File

@@ -513,9 +513,11 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction)
msg = message->data;
if (msg->header.type != PTP_TYPE_DELAY_REQ || msg->header.version != PTP_VERSION ||
if ((msg->header.type != PTP_TYPE_DELAY_REQ && msg->header.type != PTP_TYPE_SYNC) ||
(msg->header.version != PTP_VERSION_2 &&
(msg->header.version != PTP_VERSION_2_1 || msg->header.min_sdoid != 0)) ||
ntohs(msg->header.length) != message->length ||
msg->header.domain != PTP_DOMAIN_NTP ||
msg->header.domain != CNF_GetPtpDomain() ||
ntohs(msg->header.flags) != PTP_FLAG_UNICAST ||
ntohs(msg->tlv_header.type) != PTP_TLV_NTP ||
ntohs(msg->tlv_header.length) != message->length - PTP_NTP_PREFIX_LENGTH) {
@@ -561,9 +563,9 @@ wrap_message(SCK_Message *message, int sock_fd)
memset(ptp_message, 0, PTP_NTP_PREFIX_LENGTH);
ptp_message->header.type = PTP_TYPE_DELAY_REQ;
ptp_message->header.version = PTP_VERSION;
ptp_message->header.version = PTP_VERSION_2;
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
ptp_message->header.domain = PTP_DOMAIN_NTP;
ptp_message->header.domain = CNF_GetPtpDomain();
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
ptp_message->header.sequence_id = htons(sequence_id++);
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);

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

@@ -99,6 +99,9 @@ static int sock_fd;
/* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled;
/* Flag limiting logging of connection error messages */
static int logged_connection_error;
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
@@ -134,6 +137,14 @@ open_socket(void)
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
if (sock_fd < 0) {
sock_fd = INVALID_SOCK_FD;
/* Log an error only once before a successful exchange to avoid
flooding the system log */
if (!logged_connection_error) {
LOG(LOGS_ERR, "Could not connect to signd socket %s : %s", path, strerror(errno));
logged_connection_error = 1;
}
return 0;
}
@@ -160,6 +171,8 @@ process_response(SignInstance *inst)
return;
}
logged_connection_error = 0;
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG("Invalid NTP socket");

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2023
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 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
@@ -61,6 +61,8 @@ typedef struct {
(may be an IP address) */
IPAddr resolved_addr; /* Address resolved from the name, which can be
different from remote_addr (e.g. NTS-KE) */
int family; /* IP family of acceptable resolved addresses
(IPADDR_UNSPEC if any) */
int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response
@@ -98,6 +100,8 @@ struct UnresolvedSource {
int pool_id;
/* Name to be resolved */
char *name;
/* Address family to filter resolved addresses */
int family;
/* Flag indicating addresses should be used in a random order */
int random_order;
/* Flag indicating current address should be replaced only if it is
@@ -215,8 +219,14 @@ NSR_Finalise(void)
ARR_DestroyInstance(pools);
SCH_RemoveTimeout(resolving_id);
while (unresolved_sources)
remove_unresolved_source(unresolved_sources);
/* Leave the unresolved sources allocated if the async resolver is running
to avoid reading the name from freed memory. The handler will not be
called as the scheduler should no longer be running at this point. */
if (!resolving_source) {
while (unresolved_sources)
remove_unresolved_source(unresolved_sources);
}
initialised = 0;
}
@@ -353,7 +363,7 @@ log_source(SourceRecord *record, int addition, int once_per_pool)
/* Procedure to add a new source */
static NSR_Status
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
add_source(NTP_Remote_Address *remote_addr, char *name, int family, NTP_Source_Type type,
SourceParameters *params, int pool_id, uint32_t conf_id)
{
SourceRecord *record;
@@ -391,6 +401,7 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->resolved_addr = remote_addr->ip_addr;
record->family = family;
record->pool_id = pool_id;
record->tentative = 1;
record->conf_id = conf_id;
@@ -446,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)
@@ -552,6 +562,10 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
/* Skip addresses not from the requested family */
if (us->family != IPADDR_UNSPEC && us->family != new_addr.ip_addr.family)
continue;
if (us->pool_id != INVALID_POOL) {
/* In the pool resolving mode, try to replace a source from
the pool which does not have a real address yet */
@@ -629,13 +643,16 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
next = us->next;
/* Don't repeat the resolving if it (permanently) failed, it was a
replacement of a real address, or all addresses are already resolved */
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us))
replacement of a real address, a refreshment, or all addresses are
already resolved */
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) ||
us->refreshment || is_resolved(us))
remove_unresolved_source(us);
/* If a restart was requested and this was the last source in the list,
start with the first source again (if there still is one) */
if (!next && resolving_restart) {
DEBUG_LOG("Restarting");
next = unresolved_sources;
resolving_restart = 0;
}
@@ -700,11 +717,15 @@ static void
append_unresolved_source(struct UnresolvedSource *us)
{
struct UnresolvedSource **i;
int n;
for (i = &unresolved_sources; *i; i = &(*i)->next)
for (i = &unresolved_sources, n = 0; *i; i = &(*i)->next, n++)
;
*i = us;
us->next = NULL;
DEBUG_LOG("Added unresolved source #%d pool_id=%d random=%d refresh=%d",
n + 1, us->pool_id, us->random_order, us->refreshment);
}
/* ================================================== */
@@ -754,8 +775,19 @@ static int get_unused_pool_id(void)
static uint32_t
get_next_conf_id(uint32_t *conf_id)
{
SourceRecord *record;
unsigned int i;
again:
last_conf_id++;
/* Make sure the ID is not already used (after 32-bit wraparound) */
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr && record->conf_id == last_conf_id)
goto again;
}
if (conf_id)
*conf_id = last_conf_id;
@@ -768,14 +800,14 @@ NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
return add_source(remote_addr, NULL, type, params, INVALID_POOL,
return add_source(remote_addr, NULL, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
/* ================================================== */
NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
struct UnresolvedSource *us;
@@ -787,7 +819,9 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
/* If the name is an IP address, add the source with the address directly */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
return add_source(&remote_addr, name, type, params, INVALID_POOL,
if (family != IPADDR_UNSPEC && family != remote_addr.ip_addr.family)
return NSR_InvalidAF;
return add_source(&remote_addr, name, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
@@ -799,6 +833,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->family = family;
us->random_order = 0;
us->refreshment = 0;
@@ -835,7 +870,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
for (i = 0; i < new_sources; i++) {
if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id;
if (add_source(&remote_addr, name, type, params, us->pool_id, cid) != NSR_Success)
if (add_source(&remote_addr, name, family, type, params, us->pool_id, cid) != NSR_Success)
return NSR_TooManySources;
}
@@ -1026,6 +1061,7 @@ resolve_source_replacement(SourceRecord *record, int refreshment)
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
us->family = record->family;
/* Ignore the order of addresses from the resolver to not get
stuck with a pair of unreachable or otherwise unusable servers
(e.g. falsetickers) in case the order doesn't change, or a group
@@ -1036,7 +1072,10 @@ resolve_source_replacement(SourceRecord *record, int refreshment)
us->address = *record->remote_addr;
append_unresolved_source(us);
NSR_ResolveSources();
/* Don't restart resolving round if already running */
if (!resolving_source)
NSR_ResolveSources();
}
/* ================================================== */
@@ -1431,6 +1470,20 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
/* ================================================== */
int
NSR_ModifyOffset(IPAddr *address, double new_offset)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyOffset(get_record(slot)->data, new_offset);
return 1;
}
/* ================================================== */
int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{

View File

@@ -55,9 +55,12 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
/* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. If the
name is an address, it is equivalent to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
intervals until it succeeds or fails with a non-temporary error. The
specified family filters resolved addresses. If the name is an address
and its family does not conflict with the specified family, it is equivalent
to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int family, int port, int pool,
NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
extern const char *NSR_StatusToString(NSR_Status status);
@@ -137,6 +140,8 @@ extern int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_rati
extern int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum);
extern int NSR_ModifyOffset(IPAddr *address, double new_offset);
extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);

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
@@ -242,7 +242,7 @@ accept_connection(int listening_fd, int event, void *arg)
SCH_GetLastEventTime(&now, NULL, NULL);
log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now);
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index)) {
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index) != CLG_PASS) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "rate limit");
SCK_CloseSocket(sock_fd);
@@ -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,
@@ -279,7 +278,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
}
/* NTS NAK response does not have any other fields */
if (kod)
if (kod == NTP_KOD_NTS_NAK)
return 1;
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {

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 */
@@ -111,7 +111,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
{ 0, 0 }, /* LOCAL2 - not supported */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
@@ -130,6 +130,8 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
REQ_LENGTH_ENTRY(modify_offset, null), /* MODIFY_OFFSET */
REQ_LENGTH_ENTRY(local, null), /* LOCAL3 */
};
static const uint16_t reply_lengths[] = {
@@ -149,7 +151,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
0, /* SERVER_STATS - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
0, /* NTP_DATA - not supported */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
@@ -159,6 +161,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
0, /* SERVER_STATS3 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA2 */
};
/* ================================================== */

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);

5
ptp.h
View File

@@ -31,9 +31,10 @@
#include "ntp.h"
#define PTP_VERSION 2
#define PTP_VERSION_2 2
#define PTP_VERSION_2_1 (2 | 1 << 4)
#define PTP_TYPE_SYNC 0
#define PTP_TYPE_DELAY_REQ 1
#define PTP_DOMAIN_NTP 123
#define PTP_FLAG_UNICAST (1 << (2 + 8))
#define PTP_TLV_NTP 0x2023

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);
}
@@ -166,8 +170,8 @@ RCL_AddRefclock(RefclockParameters *params)
if (!inst->driver->init && !inst->driver->poll)
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
if (params->tai && !CNF_GetLeapSecTimezone())
LOG_FATAL("refclock tai option requires leapsectz");
if (params->tai && !CNF_GetLeapSecList() && !CNF_GetLeapSecTimezone())
LOG_FATAL("refclock tai option requires leapseclist or leapsectz");
inst->data = NULL;
inst->driver_parameter = Strdup(params->driver_parameter);
@@ -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,
@@ -321,6 +313,22 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
}
}
int
RCL_ModifyOffset(uint32_t ref_id, double offset)
{
unsigned int i;
for (i = 0; i < ARR_GetSize(refclocks); i++) {
RCL_Instance inst = get_refclock(i);
if (inst->ref_id == ref_id) {
inst->offset = offset;
LOG(LOGS_INFO, "Source %s new offset %f", UTI_RefidToString(ref_id), offset);
return 1;
}
}
return 0;
}
void
RCL_SetDriverData(RCL_Instance instance, void *data)
{
@@ -649,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)
{
@@ -776,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;
@@ -791,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);
@@ -800,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

@@ -68,6 +68,7 @@ extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int RCL_ModifyOffset(uint32_t ref_id, double offset);
/* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
@@ -80,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;
@@ -175,7 +177,7 @@ static void read_ext_pulse(int fd, int event, void *anything)
instance = anything;
phc1 = RCL_GetDriverData(instance);
/* The Linux kernel (as of 6.2) has one shared queue of timestamps for all
/* Linux versions before 6.7 had one shared queue of timestamps for all
descriptors of the same PHC. Search for all refclocks that expect
the timestamp. */
@@ -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);
}

179
refclock_rtc.c Normal file
View File

@@ -0,0 +1,179 @@
/*
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->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

@@ -4,6 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
* Copyright (C) Andy Fiddaman 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
@@ -33,6 +34,7 @@
#include "reference.h"
#include "util.h"
#include "conf.h"
#include "leapdb.h"
#include "logging.h"
#include "local.h"
#include "sched.h"
@@ -45,14 +47,15 @@
/* 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;
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;
@@ -61,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;
@@ -106,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);
@@ -122,9 +127,6 @@ static int leap_in_progress;
/* Timer for the leap second handler */
static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname;
/* ================================================== */
static LOG_FileID logfileid;
@@ -155,7 +157,6 @@ static int ref_adjustments;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */
@@ -195,7 +196,6 @@ REF_Initialise(void)
FILE *in;
double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm;
int tai_offset;
mode = REF_ModeNormal;
are_we_synchronised = 0;
@@ -211,9 +211,10 @@ REF_Initialise(void)
our_frequency_sd = 0.0;
our_offset_sd = 0.0;
drift_file_age = 0.0;
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) {
@@ -249,8 +250,12 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
&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;
@@ -260,18 +265,6 @@ REF_Initialise(void)
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
leap_mode = REF_LeapModeStep;
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
} else {
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
}
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
@@ -593,77 +586,6 @@ is_leap_second_day(time_t when)
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when, int *tai_offset)
{
static time_t last_tz_leap_check;
static NTP_Leap tz_leap;
static int tz_tai_offset;
struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
*tai_offset = tz_tai_offset;
/* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600);
if (last_tz_leap_check == when)
return tz_leap;
last_tz_leap_check = when;
tz_leap = LEAP_Normal;
tz_tai_offset = 0;
tm = gmtime(&when);
if (!tm)
return tz_leap;
stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
if (tz_env) {
if (strlen(tz_env) >= sizeof (tz_orig))
return tz_leap;
strcpy(tz_orig, tz_env);
}
setenv("TZ", leap_tzname, 1);
tzset();
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
t = mktime(&stm);
if (t != -1)
tz_tai_offset = t - when + 10;
/* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60;
stm.tm_min = 59;
stm.tm_hour = 23;
t = mktime(&stm);
if (tz_env)
setenv("TZ", tz_orig, 1);
else
unsetenv("TZ");
tzset();
if (t == -1)
return tz_leap;
if (stm.tm_sec == 60)
tz_leap = LEAP_InsertSecond;
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
*tai_offset = tz_tai_offset;
return tz_leap;
}
/* ================================================== */
static void
leap_end_timeout(void *arg)
{
@@ -751,16 +673,16 @@ set_leap_timeout(time_t now)
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{
NTP_Leap tz_leap;
NTP_Leap ldb_leap;
int leap_sec, tai_offset;
leap_sec = 0;
tai_offset = 0;
if (leap_tzname && now) {
tz_leap = get_tz_leap(now, &tai_offset);
if (now) {
ldb_leap = LDB_GetLeap(now, &tai_offset);
if (leap == LEAP_Normal)
leap = tz_leap;
leap = ldb_leap;
}
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
@@ -1069,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);
@@ -1087,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;
}
@@ -1177,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);
@@ -1219,21 +1146,30 @@ REF_GetReferenceParams
double *root_dispersion
)
{
double dispersion, delta;
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;
if (local_activate == 0.0 || (are_we_synchronised && distance < local_activate))
local_activate_ok = 1;
/* Local reference is active when enabled and the clock is not synchronised
or the root distance exceeds the threshold */
if (are_we_synchronised &&
!(enable_local_stratum && our_root_delay / 2 + dispersion > local_distance)) {
!(enable_local_stratum && local_activate_ok && wait_local_ok &&
distance > local_distance)) {
*is_synchronised = 1;
@@ -1245,7 +1181,7 @@ REF_GetReferenceParams
*root_delay = our_root_delay;
*root_dispersion = dispersion;
} else if (enable_local_stratum) {
} else if (enable_local_stratum && local_activate_ok && wait_local_ok) {
*is_synchronised = 0;
@@ -1345,12 +1281,16 @@ REF_ModifyMakestep(int limit, double threshold)
/* ================================================== */
void
REF_EnableLocal(int stratum, double distance, int orphan)
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");
}
@@ -1368,7 +1308,7 @@ REF_DisableLocal(void)
#define LEAP_SECOND_CLOSE 5
static int
is_leap_close(time_t t)
is_leap_close(double t)
{
return leap_when != 0 &&
t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
@@ -1398,7 +1338,7 @@ REF_GetTaiOffset(struct timespec *ts)
{
int tai_offset;
get_tz_leap(ts->tv_sec, &tai_offset);
LDB_GetLeap(ts->tv_sec, &tai_offset);
return tai_offset;
}

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);
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

View File

@@ -377,7 +377,7 @@ find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
r = v;
do {
while (l < v && x[l] < piv) l++;
while (x[r] > piv) r--;
while (r > 0 && x[r] > piv) r--;
if (r <= l) break;
EXCH(x[l], x[r]);
l++;

View File

@@ -181,6 +181,10 @@ typedef struct {
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
uint32_t total_kernel_tx_ts;
uint32_t total_kernel_rx_ts;
uint32_t total_hw_tx_ts;
uint32_t total_hw_rx_ts;
} RPT_NTPReport;
typedef struct {

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

@@ -296,14 +296,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 +352,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 +497,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 +529,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 +578,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 +599,7 @@ static void
measurement_timeout(void *any)
{
timeout_id = 0;
switch_interrupts(1);
RTC_Linux_SwitchInterrupt(fd, 1);
}
/* ================================================== */
@@ -586,21 +607,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 +760,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,63 +772,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_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:
@@ -835,7 +856,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);
}
@@ -847,7 +868,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);
}
@@ -855,7 +876,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);
@@ -877,7 +898,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);
}
/* ================================================== */
@@ -910,6 +931,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
@@ -919,12 +967,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();
@@ -937,66 +983,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;
}
@@ -1064,7 +1085,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 */

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);

196
sources.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2023
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 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
@@ -66,10 +66,10 @@ 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_UNSYNCHRONISED, /* Provides samples but not unsynchronised */
SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_UNSYNCHRONISED, /* Provides samples, but not synchronised */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
@@ -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];
};
/* ================================================== */
@@ -177,6 +177,8 @@ static int reported_no_majority; /* Flag to avoid repeated log message
static int report_selection_loss; /* Flag to force logging a message if
selection is lost in a transient state
(SRC_WAITS_STATS, SRC_WAITS_UPDATE) */
static int forced_first_report; /* Flag to allow one failed selection to be
logged before a successful selection */
/* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0
@@ -204,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);
@@ -320,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;
@@ -338,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);
}
@@ -358,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);
@@ -524,11 +524,6 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++;
if (!reachable && inst->index == selected_source_index) {
/* Try to select a better source */
SRC_SelectSource(NULL);
}
/* Check if special reference update mode failed */
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
REF_SetUnsynchronised();
@@ -537,6 +532,10 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
/* Try to replace unreachable NTP sources */
if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS)
handle_bad_source(inst);
/* Source selection can change with unreachable sources */
if (inst->reachability == 0)
SRC_SelectSource(NULL);
}
/* ================================================== */
@@ -619,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;
@@ -642,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;
}
/* ================================================== */
@@ -685,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;
@@ -730,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)
{
@@ -738,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);
}
}
@@ -748,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;
}
}
@@ -862,7 +934,8 @@ SRC_SelectSource(SRC_Instance updated_inst)
struct SelectInfo *si;
struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
int max_badstat_reach, max_badstat_reach_size, n_badstats_sources;
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;
@@ -879,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;
}
@@ -891,9 +964,10 @@ 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 = 0;
max_sel_reach_size = max_badstat_reach_size = 0;
max_reach_sample_ago = 0.0;
for (i = 0; i < n_sources; i++) {
@@ -913,11 +987,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* Ignore sources which are not synchronised */
if (sources[i]->leap == LEAP_Unsynchronised) {
mark_source(sources[i], SRC_UNSYNCHRONISED);
continue;
}
/* 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,
@@ -930,6 +1002,14 @@ SRC_SelectSource(SRC_Instance updated_inst)
mark_source(sources[i], SRC_BAD_STATS);
if (max_badstat_reach < sources[i]->reachability)
max_badstat_reach = sources[i]->reachability;
if (max_badstat_reach_size < sources[i]->reachability_size)
max_badstat_reach_size = sources[i]->reachability_size;
continue;
}
/* Ignore sources which are not synchronised */
if (sources[i]->leap == LEAP_Unsynchronised) {
mark_source(sources[i], SRC_UNSYNCHRONISED);
continue;
}
@@ -1064,13 +1144,22 @@ 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;
}
/* Wait for a source to have full reachability register to allow one
failed selection to be logged before first successful selection */
if (!forced_first_report &&
MAX(max_sel_reach_size, max_badstat_reach_size) == SOURCE_REACH_BITS) {
report_selection_loss = 1;
forced_first_report = 1;
}
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;
}
@@ -1144,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;
}
@@ -1200,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);
@@ -1317,23 +1413,23 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Before selecting the new synchronisation source wait until the reference
can be updated */
if (sources[max_score_index]->updates == 0) {
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;
report_selection_loss = 0;
forced_first_report = 1;
}
mark_source(sources[selected_source_index], SRC_SELECTED);
@@ -1796,10 +1892,10 @@ get_status_char(SRC_Status status)
switch (status) {
case SRC_UNSELECTABLE:
return 'N';
case SRC_UNSYNCHRONISED:
return 's';
case SRC_BAD_STATS:
return 'M';
case SRC_UNSYNCHRONISED:
return 's';
case SRC_BAD_DISTANCE:
return 'd';
case SRC_JITTERY:

View File

@@ -549,9 +549,9 @@ SST_DoNewRegression(SST_Stats inst)
sd_weight += (peer_distances[i] - min_distance) / sd;
weights[i] = SQUARE(sd_weight);
}
}
correct_asymmetry(inst, times_back, offsets);
correct_asymmetry(inst, times_back, offsets);
}
inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples,
offsets + inst->runs_samples, weights,

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,253 +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 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_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)
@@ -419,6 +112,12 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
memset(report, 0, sizeof (*report));
}
int
RCL_ModifyOffset(uint32_t ref_id, double offset)
{
return 0;
}
#endif /* !FEAT_REFCLOCK */
#ifndef FEAT_SIGND

View File

@@ -38,6 +38,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 +54,6 @@
#ifdef FEAT_RTC
#include <linux/rtc.h>
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
#include <linux/sockios.h>
#endif
#endif
#ifdef FEAT_PRIVDROP
@@ -64,6 +67,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 +906,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;
}
@@ -990,6 +1061,14 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
return 0;
}
#if defined(PTP_MASK_CLEAR_ALL) && defined(PTP_MASK_EN_SINGLE)
/* Disable events from other channels on this descriptor */
if (ioctl(fd, PTP_MASK_CLEAR_ALL))
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_MASK_CLEAR_ALL", strerror(errno));
else if (ioctl(fd, PTP_MASK_EN_SINGLE, &channel))
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_MASK_EN_SINGLE", strerror(errno));
#endif
return 1;
}

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

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

@@ -66,10 +66,9 @@ get_tempcomp(double temp)
return k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2;
/* Otherwise interpolate/extrapolate between two nearest points */
for (i = 1; i < ARR_GetSize(points); i++) {
p2 = (struct Point *)ARR_GetElement(points, i);
if (p2->temp >= temp)
for (i = 1; ; i++) {
p2 = ARR_GetElement(points, i);
if (p2->temp >= temp || i + 1 >= ARR_GetSize(points))
break;
}
p1 = p2 - 1;

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

@@ -3,6 +3,7 @@
cd ../..
for opts in \
"--enable-debug" \
"--host-system=Linux" \
"--host-system=NetBSD" \
"--host-system=FreeBSD" \

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 ../..

View File

@@ -41,7 +41,6 @@ for time_offset in -1e-1 1e-1; do
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync && test_fail
done

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

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

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

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
. ./test.common
test_start "IPv6 addressing"
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

@@ -35,11 +35,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 " 650 750 refclocks.log || test_fail
else
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
fi
check_file_messages "20.* GPS.*[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
@@ -64,7 +60,7 @@ Stratum.*: 1
Root delay : 0\.000000001 seconds
.*$" || test_fail
check_file_messages "20.* PPS1.*[0-9] N " 620 740 refclocks.log || test_fail
check_file_messages "20.* PPS1.*[0-9] N " 610 740 refclocks.log || test_fail
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
@@ -89,14 +85,15 @@ 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=100
max_sync_time=220
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
logdir tmp
log refclocks"
log refclocks
maxupdateskew 10000"
run_test || test_fail
check_chronyd_exit || test_fail
@@ -108,11 +105,63 @@ Stratum.*: 1
Root delay : 0\.000000001 seconds
.*$" || test_fail
check_file_messages "20.* PPS1.*[0-9] N " 800 940 refclocks.log || test_fail
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

@@ -21,6 +21,7 @@ EOF
clients=2
peers=2
freq_max_limit=1e-3
max_sync_time=1000
client_server_options="minpoll 6 maxpoll 6"
client_peer_options="minpoll 6 maxpoll 6"
@@ -30,6 +31,18 @@ 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

@@ -58,7 +58,7 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
\^\? 192\.168\.123\.2 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][012]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.2 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms
210 n_samples = 0
@@ -114,7 +114,7 @@ limit=1
for chronyc_conf in \
"accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324 ipv6 ipv4" \
"add server node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \
@@ -145,7 +145,7 @@ for chronyc_conf in \
"dfreq 1.0e-3" \
"doffset -1.0" \
"dump" \
"local stratum 5 distance 1.0 orphan" \
"local stratum 5 distance 1.0 activate 0.5 orphan waitsynced 100 waitunsynced 20" \
"local off" \
"makestep 10.0 3" \
"makestep" \
@@ -165,6 +165,7 @@ for chronyc_conf in \
"offline" \
"offline 255.255.255.0/1.2.3.0" \
"offline 1.2.3.0/24" \
"offset 1.2.3.4 1.0" \
"online" \
"online 1.2.3.0/24" \
"onoffline" \
@@ -194,6 +195,33 @@ 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
done
server_conf="server 192.168.123.1"
cmdmon_unix=1
chronyc_conf="
@@ -241,12 +269,16 @@ 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 : [01]
Total HW TX : 0
Total HW RX : 0
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
@@ -261,9 +293,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
@@ -347,6 +379,7 @@ maxpoll 192.168.123.1 5
maxupdateskew 192.168.123.1 10.0
minpoll 192.168.123.1 3
minstratum 192.168.123.1 1
offset 192.168.123.1 -1.0
polltarget 192.168.123.1 10
selectopts 192.168.123.1 +trust +prefer -require
selectdata
@@ -371,6 +404,7 @@ check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
200 OK
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N \-PT\-\- \-PT\-\- 0 1\.0 \+0ns \+0ns \?
@@ -385,7 +419,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
@@ -433,7 +467,12 @@ server_conf="
server 192.168.123.1
noclientlog"
commands=(
check_config_h 'FEAT_IPV6 1' && commands=(
"add server ::1 ipv4" "^515 Invalid address family$"
) || commands=()
commands+=(
"add server 192.168.123.1 ipv6" "^515 Invalid address family$"
"add server nosuchnode.net1.clk" "^Invalid host/IP address$"
"allow nosuchnode.net1.clk" "^Could not read address$"
"allow 192.168.123.0/2 4" "^Could not read address$"

View File

@@ -8,54 +8,86 @@ check_config_h 'FEAT_REFCLOCK 1' || test_skip
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
leap=$[2 * 24 * 3600]
limit=$[4 * 24 * 3600]
client_start=$[2 * 3600]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
refclock_jitter=1e-9
refclock_offset="(* -1.0 (equal 0.1 (max (sum 1.0) $leap) $leap))"
for leapmode in system step slew; do
client_conf="leapsecmode $leapmode"
if [ $leapmode = slew ]; then
max_sync_time=$[$leap + 12]
else
max_sync_time=$[$leap]
fi
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
client_server_options="trust"
client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_options=""
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
for smoothmode in "" "leaponly"; do
for dir in "+1" "-1"; do
leap=$[2 * 24 * 3600 + 1 + $dir]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC
leapsecmode slew
smoothtime 400 0.001 $smoothmode"
leapseclist tmp/leap.list"
refclock_offset="(* $dir (equal 0.1 (max (sum 1.0) $leap) $leap))"
cat > tmp/leap.list <<-EOF
#$ 3676924800
#@ 3928521600
3345062400 33 # 1 Jan 2006
3439756800 $[33 - $dir] # 1 Jan 2009 $(
[ "$dir" = "+1" ] && echo -e "\n3471292800 33\n3502828800 34")
3550089600 35 # 1 Jul 2012
EOF
for leapmode in system step slew; do
client_conf="leapsecmode $leapmode"
if [ $leapmode = slew ]; then
max_sync_time=$[2 * 24 * 3600 + 13]
else
max_sync_time=$[2 * 24 * 3600 + 1]
fi
min_sync_time=$[$max_sync_time - 2]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_file_messages "System clock TAI offset set to" 1 1 log.1 || test_fail
check_file_messages "System clock TAI offset set to 33" 1 1 log.1 || test_fail
done
client_server_options="trust"
client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
min_sync_time=$[$leap - 2]
max_sync_time=$[$leap]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_options=""
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
for smoothmode in "" "leaponly"; do
server_conf="refclock SHM 0 dpoll 10 poll 10
leapseclist tmp/leap.list
leapsecmode slew
smoothtime 400 0.001 $smoothmode"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
done
if TZ=right/UTC date -d 'Dec 31 2008 23:59:60' 2> /dev/null | grep :60; then
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
refclock_offset="(* -1 (equal 0.1 (max (sum 1.0) $leap) $leap))"
client_conf="leapsecmode system"
min_sync_time=$[$leap - 2]
max_sync_time=$[$leap]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
fi
test_pass

View File

@@ -14,7 +14,6 @@ client_server_options="maxpoll 6 maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevrati
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail

View File

@@ -14,7 +14,7 @@ max_sync_time=800
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_source_selection && test_fail
check_sync || test_fail
limit=10000

141
test/simulation/121-local Executable file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/env bash
. ./test.common
test_start "local options"
check_config_h 'FEAT_CMDMON 1' || test_skip
server_strata=3
server_conf="local stratum 5 orphan waitunsynced 0
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
max_sync_time=900
client_start=140
chronyc_start=700
chronyc_conf="tracking"
time_rms_limit=5e-4
run_test || test_fail
check_chronyd_exit || test_fail
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
tracking
tracking
tracking
tracking"
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* 990
(equal 0.1 from 3))
(* -1
(equal 0.1 from 1)
(equal 0.1 (max (% time 2000) 1000) 1000)))
EOF
)
client_conf="local
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*7F7F0101.*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
client_conf="local distance 0.5
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*7F7F0101.*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
client_conf="local distance 2.0
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*7F7F0101.*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
client_conf="local activate 1e-4
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.* 00000000 .*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
client_conf="local activate 1e-1
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.* 00000000 .*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
client_conf="local activate 1e-1 distance 2.0
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.* 00000000 .*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
test_pass

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env bash
. ./test.common
test_start "orphan option"
check_config_h 'FEAT_CMDMON 1' || test_skip
server_strata=3
server_conf="local stratum 5 orphan
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
max_sync_time=900
client_start=140
chronyc_start=700
chronyc_conf="tracking"
time_rms_limit=5e-4
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
test_pass

View File

@@ -53,7 +53,6 @@ for rpoll in 4 5 6; do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if [ $rpoll -le 5 ]; then

View File

@@ -18,9 +18,17 @@ servers=0
refclock_offset="(+ -34 (equal 0.1 (max (sum 1.0) $leap) $leap))"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
leapsectz right/UTC
leapseclist tmp/leap.list
leapsecmode ignore
maxchange 1e-3 1 0"
maxchange 1e-3 10 0"
cat > tmp/leap.list <<-EOF
#$ 3676924800
#@ 3928521600
3345062400 33 # 1 Jan 2006
3439756800 34 # 1 Jan 2009
3550089600 35 # 1 Jul 2012
EOF
run_test || test_fail
check_chronyd_exit || test_fail
@@ -33,9 +41,9 @@ time_offset=-1000
refclock_offset="(+ -34)"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
leapsectz right/UTC
leapseclist tmp/leap.list
makestep 1 1
maxchange 1e-3 1 0"
maxchange 1e-3 10 0"
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -22,7 +22,7 @@ client_min_mean_out_interval=150.0
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail

View File

@@ -12,7 +12,7 @@ client_min_mean_out_interval=15.9
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail

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

@@ -15,4 +15,15 @@ check_sync || test_fail
check_file_messages " 2 1 " 1200 1300 log.packets || test_fail
check_file_messages " 1 2 " 180 220 log.packets || test_fail
server_conf="ratelimit interval 6 burst 2 leak 4 kod 2"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_file_messages " 2 1 " 700 850 log.packets || test_fail
check_file_messages " 1 2 " 350 450 log.packets || test_fail
check_log_messages "Received KoD RATE.*\.123.1" 100 140 || test_fail
test_pass

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