Compare commits

...

434 Commits
3.0 ... 3.5.1

Author SHA1 Message Date
Miroslav Lichvar
04328ceead doc: update NEWS 2020-08-19 16:24:04 +02:00
Miroslav Lichvar
f00fed2009 main: create new file when writing pidfile
When writing the pidfile, open the file with the O_CREAT|O_EXCL flags
to avoid following a symlink and writing the PID to an unexpected file,
when chronyd still has the root privileges.

The Linux open(2) man page warns about O_EXCL not working as expected on
NFS versions before 3 and Linux versions before 2.6. Saving pidfiles on
a distributed filesystem like NFS is not generally expected, but if
there is a reason to do that, these old kernel and NFS versions are not
considered to be supported for saving files by chronyd.

This is a minimal backport specific to this issue of the following
commits:
- commit 2fc8edacb8 ("use PATH_MAX")
- commit f4c6a00b2a ("logging: call exit() in LOG_Message()")
- commit 7a4c396bba ("util: add functions for common file operations")
- commit e18903a6b5 ("switch to new util file functions")

Reported-by: Matthias Gerstner <mgerstner@suse.de>
2020-08-06 11:46:04 +02:00
Miroslav Lichvar
ffb9887cce doc: update NEWS 2019-05-10 12:22:57 +02:00
Miroslav Lichvar
9220c9b8a2 update copyright years 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
2e28b19112 doc: add note about minsamples to FAQ 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
636a4e2794 refclock: remove unnecessary strlen() call 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
5c9e1e0b69 test: extend 133-hwtimestamp test 2019-05-10 11:01:27 +02:00
Miroslav Lichvar
64fd1b8ba5 ntp: check value returned by CMSG_FIRSTHDR
In NIO_Linux_RequestTxTimestamp(), check the returned pointer and the
length of the buffer before adding the control message. This fixes an
issue reported by the Clang static analyzer.
2019-05-10 10:58:37 +02:00
Miroslav Lichvar
69d3913f3e ntp: check timestamping configuration when SIOCSHWTSTAMP fails
With future kernels it may be possible to get, but not set, the HW
timestamping configuration on some specific interfaces like macvlan in
containers. This would require the admin to configure the timestamping
before starting chronyd.

If SIOCSHWTSTAMP failed on an interface, try SIOCGHWTSTAMP to check if
the current configuration matches the expected configuration and allow
the interface to be used for HW timestamping.
2019-05-09 14:44:58 +02:00
Miroslav Lichvar
08fd011b6a examples: remove /var from PIDFile in chronyd.service
Recent systemd versions complain when loading a unit using a PIDFile
that relies on the /var/run -> /run symlink.
2019-05-06 15:44:24 +02:00
Miroslav Lichvar
c172268cfe doc: update NEWS 2019-05-02 11:50:41 +02:00
Miroslav Lichvar
94b014865c doc: add more recommendations for best stability to FAQ 2019-04-30 14:46:19 +02:00
Miroslav Lichvar
099aaf2cb1 doc: update list of contributors
Include all authors from the git repository.
2019-04-30 12:56:25 +02:00
Miroslav Lichvar
4481a8b24f doc: simplify acknowledgements in README
Stop trying to maintain a list of individual contributions. Just list
the contributors. For tracking individual changes in the source code
there is git.
2019-04-30 12:56:25 +02:00
Miroslav Lichvar
b626fe661e doc: list build requirements in installation 2019-04-30 12:56:25 +02:00
Miroslav Lichvar
ba8fcd145d doc: improve combinelimit description 2019-04-26 11:19:20 +02:00
Miroslav Lichvar
981d09de40 doc: improve rtconutc description 2019-04-26 11:12:41 +02:00
Miroslav Lichvar
86a99bb257 test: use env in shebang of system tests
This should allow the tests to run on systems where bash is not in /bin.
2019-04-26 10:54:02 +02:00
Miroslav Lichvar
3093a11cd0 test: add 104-systemdirs system test 2019-04-25 18:58:01 +02:00
Miroslav Lichvar
058b788d38 test: fix owner of driftfile and keys in system tests 2019-04-25 18:57:39 +02:00
Miroslav Lichvar
66a42fa493 test: allow separate lib/log/run directories in system tests 2019-04-25 18:55:56 +02:00
Miroslav Lichvar
a85f63cc15 test: check if non-root user can access test directory 2019-04-25 18:24:50 +02:00
Miroslav Lichvar
bbe1e69dcc test: redirect error messages in system tests 2019-04-25 18:24:41 +02:00
Miroslav Lichvar
1b52bba7b9 test: allow TEST_DIR and CHRONYC_WRAPPER to be set for system tests 2019-04-25 17:29:34 +02:00
Stefan R. Filipek
c5c80ef400 sys_posix: support SCHED_FIFO and mlockall on more OSs
Real-time scheduling and memory locking is available on posix compliant
OSs. This patch centralizes this functionality and brings support to
FreeBSD, NetBSD, and Solaris.

[ML: updated coding style]
2019-04-24 12:18:07 +02:00
Miroslav Lichvar
a78031ce0d refclock: check all driver options
In each driver provide a list of supported options and abort when an
unknown option is specified in the refclock directive.
2019-04-18 16:27:47 +02:00
Miroslav Lichvar
34e9dd13ce doc: fix syntax of refclock directive
When multiple driver options are specified, they need to be separated by
colon, not comma.
2019-04-18 16:27:19 +02:00
Miroslav Lichvar
6e52a9be7a test: add system tests
Add a new set of tests for testing basic functionality, starting chronyd
with root privileges on the actual system instead of the simulator.

Tests numbered in the 100-199 range are considered destructive and
intended to be used only on machines dedicated for development or
testing. They are started by the run script only with the -d option.
They may adjust/step the system clock and other clocks, block the RTC,
enable HW timestamping, create SHM segments, etc.

Other tests should not interfere with the system and should work even
when another NTP server/client is running.
2019-04-18 16:11:45 +02:00
Stefan R. Filipek
69c6dffd63 sys_linux: use pthread_setschedparam instead of sched_setscheduler
Fix an issue with Linux and musl libc where sched_setscheduler is not
implemented. It seems that pthread_setschedparam is more widely
supported across different C libraries and OSs. For our use case, it
should make no difference which call is used.
2019-04-08 16:38:38 +02:00
Vincent Blut
2ddd0ae231 sys_linux: allow further syscalls in seccomp filter
These are needed on arm64.
2019-03-18 14:24:54 +01:00
Leigh Brown
79db0b7eca sys_linux: allow recv and send in seccomp filter 2019-03-14 09:07:15 +01:00
Vincent Blut
2ebba7fbaa sys_linux: allow waitpid in seccomp filter 2019-02-28 17:42:02 +01:00
Vincent Blut
e392d1fde9 sys_linux: allow _llseek in seccomp filter
This is needed on various 32-bit platforms to reposition read/write file
offset on {raw}measurements and statistics log files.
2019-02-28 16:19:05 +01:00
Miroslav Lichvar
d7c93ec950 test: fix distribution of settings in ntp_core unit test 2019-02-20 10:11:58 +01:00
Miroslav Lichvar
6af39d63aa ntp: don't use IP_SENDSRCADDR on bound socket
On FreeBSD, sendmsg() fails when IP_SENDSRCADDR specifies a source
address on a socket that is bound to the address. This prevents a server
configured with the bindaddress directive from responding to clients.

Add a new variable to check whether the server IPv4 socket is not bound
before setting the source address.
2018-12-03 16:08:08 +01:00
Miroslav Lichvar
cc8414b1b3 sys_linux: add support for PTP_SYS_OFFSET_EXTENDED ioctl
A new ioctl will probably be added in Linux 4.21. It should enable a
significantly more accurate measurement of the offset between PHC and
system clock.
2018-11-27 14:56:17 +01:00
Miroslav Lichvar
6b44055e3d sys_linux: split reading and processing of PHC samples 2018-11-27 14:51:25 +01:00
Miroslav Lichvar
9f9c6cc6ab ntp: fix transposition with timestamping packet info
Don't forget to include the length of the frame check sequence (FCS) in
the RX timestamp transposition when the L2 length of the received packet
is from SCM_TIMESTAMPING_PKTINFO.

This fixes commit 934d4047f1.
2018-10-08 15:54:07 +02:00
Bryan Christianson
f176193d35 sys_macosx: remove adjtime() check
Remove the runtime checking of adjtime(). adjtime() was broken in beta
releases of macOS 10.13 but is ok now.
2018-10-02 10:50:04 +02:00
Miroslav Lichvar
e8bc41e862 test: fix tests to skip when missing required feature 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
91dbe3c6c2 test: allow unit tests to be skipped 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
3e876d4218 test: add function for checking config.h in tests 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
31b1f2e8a1 test: include util.h for MIN macro 2018-09-27 11:42:38 +02:00
Miroslav Lichvar
4169e94b1d nameserv: adopt some include directives from sysincl.h
Move headers specific to name resolving to nameserv.c. This should hide
the system MIN/MAX macros from the rest of the code.
2018-09-20 15:34:24 +02:00
Miroslav Lichvar
948ecf8431 hash: include util.h for MIN macro
The hash_intmd5.c file inadvertently relied on the system headers to
provide the MIN macro, but it is missing with some libc implementations.
2018-09-20 15:34:24 +02:00
Miroslav Lichvar
91f3f97ea7 test: fix Makefile to not create .deps in project root 2018-09-19 16:38:15 +02:00
Miroslav Lichvar
65bb65b440 doc: add new question to FAQ 2018-09-17 18:38:46 +02:00
Miroslav Lichvar
ea6e8d85a3 doc: improve description of minsamples directive 2018-09-17 18:38:24 +02:00
Miroslav Lichvar
add932501f test: add 136-broadcast test 2018-09-17 18:36:22 +02:00
Miroslav Lichvar
89390a738f test: add 012-daemonts test 2018-09-17 18:36:22 +02:00
Miroslav Lichvar
ac4f6ab93b test: improve Makefile
The -s option of make apparently doesn't work when called from make -C.
Add another filter to ignore the Entering/Leaving messages.

Also, fix a typo.
2018-09-13 16:29:49 +02:00
Miroslav Lichvar
dbcb1b9b0b test: include all objects in prerequisites of unit tests 2018-09-13 11:23:16 +02:00
Miroslav Lichvar
6375307798 test: get list of objects from main Makefile
Instead of linking unit tests with *.o in the root directory, which may
include conflicting objects from a different configuration (e.g. hash),
add a print target to the main Makefile and use it in the unit test
Makefile to link only with objects that are relevant in the current
configuration.
2018-09-13 11:23:16 +02:00
Miroslav Lichvar
fb78e60d26 test: add 135-ratelimit test 2018-09-12 19:27:48 +02:00
Miroslav Lichvar
b822c7164f test: add 134-log test 2018-09-12 19:04:11 +02:00
Miroslav Lichvar
aa295730a0 test: extend 110-chronyc test 2018-09-12 18:17:35 +02:00
Miroslav Lichvar
69d70703b2 git: update .gitignore 2018-09-12 17:16:33 +02:00
Miroslav Lichvar
b2b6ef00dc makefile: remove gcov files and core dumps 2018-09-12 17:15:30 +02:00
Miroslav Lichvar
5dc86c236b update copyright years 2018-09-12 11:38:10 +02:00
Miroslav Lichvar
2563dd9d29 examples: drop chrony.spec
The example spec file was too limited to be recommended for use in any
rpm-based distribution, e.g. it didn't configure chronyd to drop the
root privileges.

Users that want to build a package from the latest source code should
start with the official package of their distribution.
2018-09-12 11:38:10 +02:00
Miroslav Lichvar
a899e3df33 examples: improve description in chrony.keys example 2018-09-12 11:38:08 +02:00
Miroslav Lichvar
692cea49f8 doc: warn about permissions in keyfile description 2018-09-12 10:43:01 +02:00
Miroslav Lichvar
bcedacaa3d test: fix samplefilt unit test to work with low-precision clock 2018-09-10 14:56:03 +02:00
Miroslav Lichvar
be3c1b5243 samplefilt: use SQUARE macro in SPF_CreateInstance() 2018-09-10 13:20:14 +02:00
Miroslav Lichvar
e626ec6c37 examples: update chrony.conf example for new default pidfile 2018-09-10 10:37:37 +02:00
Miroslav Lichvar
49d52b547f test: make 129-reload more reliable 2018-08-31 12:48:36 +02:00
Miroslav Lichvar
74a546a9e7 configure: fix compiler warning in pthread test code 2018-08-31 11:04:36 +02:00
Miroslav Lichvar
d1777087c1 doc: update NEWS 2018-08-31 10:11:17 +02:00
Miroslav Lichvar
cf7b5363cd test: extend 110-chronyc test 2018-08-31 09:55:43 +02:00
Miroslav Lichvar
7f3183cc72 test: extend 106-refclock test 2018-08-31 09:55:43 +02:00
Miroslav Lichvar
f1b8da085b doc: update FAQ 2018-08-30 11:56:13 +02:00
Miroslav Lichvar
09dfca49ec configure: fix detection of timepps.h on NetBSD
The header requires <time.h> for struct timespec.
2018-08-30 11:56:13 +02:00
Miroslav Lichvar
88e0ec07aa refclock: fix compiler warning on FreeBSD 2018-08-30 11:56:13 +02:00
Miroslav Lichvar
0adc8e8f92 ntp: add support for IP_RECVDSTADDR and IP_SENDSRCADDR
FreeBSD doesn't support IP_PKTINFO. Instead it provides IP_RECVDSTADDR
and IP_SENDSRCADDR, which can be used to get/set the destination/source
address.

In future IP_RECVIF and IP_SENDIF may be supported to get and set also
the interface.
2018-08-30 11:56:13 +02:00
Miroslav Lichvar
5fc7674e36 ntp: set interface index in IP*_PKTINFO when responding
When a server with multiple interfaces in the same network is sending a
response, setting the ipi_spec_dst/ipi6_addr field of the IP*_PKTINFO
control message selects the source address, but not necessarily the
interface. The packet has the expected source address, but it may be
sent by an interface that doesn't have the address.

Set the ipi_ifindex/ipi6_ifindex field to respond on the same interface
as the request was received from to avoid asymmetries in delay and
timestamping.
2018-08-30 11:56:13 +02:00
Miroslav Lichvar
018977044a test: add 133-hwtimestamp test 2018-08-30 08:08:19 +02:00
Miroslav Lichvar
cc49d8e6e6 test: add 132-logchange test 2018-08-28 18:27:43 +02:00
Miroslav Lichvar
933bd017b4 test: add 131-maxchange test 2018-08-28 18:27:43 +02:00
Miroslav Lichvar
d558b33d85 test: extend 108-peer test 2018-08-28 18:27:43 +02:00
Miroslav Lichvar
9268bf2cff test: don't override user settings with default values
This fixes commit 671daf06b8.
2018-08-28 18:27:43 +02:00
Miroslav Lichvar
dbf2c22467 test: extend ntp_core unit test 2018-08-28 18:27:03 +02:00
Miroslav Lichvar
af4fe92095 test: update hash unit test 2018-08-28 18:18:07 +02:00
Miroslav Lichvar
e034a07be8 test: enable unit tests to suspend logging 2018-08-28 18:18:07 +02:00
Miroslav Lichvar
eb8c9ad601 logging: allow reopening stderr
LOG_OpenFileLog(NULL) can be now used to reopen stderr.
2018-08-28 18:18:07 +02:00
Miroslav Lichvar
6847536669 logging: close previous file log after opening new one
Currently, the log is always opened only once, but that will change with
tests temporarily suspending logging.
2018-08-28 15:42:38 +02:00
Miroslav Lichvar
f5206db9b0 ntp: optimize MAC truncation
When generating a MAC for an NTP packet, request only the bytes that
will be sent.
2018-08-27 19:00:08 +02:00
Miroslav Lichvar
6ab2ed0da6 hash: allow truncated output
Tomcrypt, some NSS hash functions, and the internal MD5 require the
output buffer to be at least as long as the digest. To provide the same
hashing API with all four options, use an extra buffer for the digest
when necessary and copy only the requested bytes to the caller.
2018-08-27 19:00:08 +02:00
Miroslav Lichvar
7352e470e1 ntp: remove unnecessary constant 2018-08-27 17:30:47 +02:00
Miroslav Lichvar
5bc9c0d07a sources: check maximum reach size before postponing update
Don't wait for other sources to be selectable when the maximum
selectable and non-selectable reachability registers happen to match
and a register is already full (e.g. after heavy packet loss).
2018-08-27 13:33:47 +02:00
Miroslav Lichvar
a2146e82ef doc: improve description of LastRx column in chronyc sources 2018-08-27 11:26:51 +02:00
Miroslav Lichvar
6e10e6740c test: add 130-quit test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
bfaa10f2b0 test: add 129-reload test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
9f167a7997 test: add 128-nocontrol test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
6908163464 test: separate client/server chronyd options 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
671daf06b8 test: avoid using eval in shell scripts 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
b189a5386b test: extend util unit test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
7889d108c2 test: add samplefilt unit test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
3cfa8ce9d3 test: add sanitizers test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
570573fe28 test: detect configure errors in compilation test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
62b1a11736 test: add -Werror to CFLAGS in compilation test 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
c00d517e12 doc: update description of -r option 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
001f3d5e27 sourcestats: improve debug message in SST_GetTrackingData() 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
6045023a49 sources: use SQUARE macro in combine_sources() 2018-08-24 18:09:29 +02:00
Miroslav Lichvar
bba29a0ee7 samplefilt: check for non-increasing sample times
Adopt the check from the refclock code to check also samples from NTP.
2018-08-24 18:09:29 +02:00
Miroslav Lichvar
cffc856b50 test: update hwclock unit test 2018-08-21 16:54:54 +02:00
Miroslav Lichvar
419077e04b sys_linux: extend debug message 2018-08-21 15:52:35 +02:00
Miroslav Lichvar
7db9d4acea sys_linux: improve support for upcoming kernel versions
Starting with Linux 4.19, the frequency of the system clock should be
updated immediately in the system call itself, which will significantly
reduce the maximum delay of the update.

Increase the assumed tick rate in order to reduce the dispersion
accumulated by the driver when it sets the frequency.
2018-08-21 15:52:35 +02:00
Miroslav Lichvar
8d5b86efe7 test: make 121-orphan more reliable 2018-08-21 15:52:35 +02:00
Miroslav Lichvar
6cf16aea7b reference: refactor estimation of clock frequency
Reorder code in REF_SetReference(), clean it up a bit, and split off the
parts specific to the weighting and estimation of the new frequency.
2018-08-21 15:52:33 +02:00
Miroslav Lichvar
870545d3cb reference: include skew in local sync status setting 2018-08-21 12:06:57 +02:00
Miroslav Lichvar
2a030c0d0c sourcestats: include offset SD in tracking root dispersion 2018-08-21 12:06:57 +02:00
Miroslav Lichvar
0b709ab1bc util: introduce SQUARE macro 2018-08-21 12:06:57 +02:00
Miroslav Lichvar
a1f2f17385 reference: fix offset SD to include elapsed time
This should slow down corrections based on old measurements with large
estimated error in frequency.
2018-08-21 12:06:57 +02:00
Miroslav Lichvar
2240eefbd0 sources: fix combined offset SD to include elapsed time 2018-08-21 12:06:44 +02:00
Miroslav Lichvar
706d0c281a sources: combine frequencies by variance instead of skew
This seems to slightly improve the stability.
2018-08-21 10:15:19 +02:00
Miroslav Lichvar
ca73e34f30 sources: provide frequency SD to reference update 2018-08-17 17:40:06 +02:00
Miroslav Lichvar
cca2ef4649 sourcestats: provide frequency SD in tracking data 2018-08-17 17:40:06 +02:00
Miroslav Lichvar
05d9edbf8f sourcestats: replace constant with macro 2018-08-17 17:40:06 +02:00
Miroslav Lichvar
c5bdc52a59 test: don't require exit message to be on last line
This is useful with enabled debug output.
2018-08-17 17:39:50 +02:00
Miroslav Lichvar
74f0c0924a ntp: change maxdelay* info messages to use option names 2018-08-13 12:17:57 +02:00
Miroslav Lichvar
05492d1d23 test: improve 101-poll test 2018-08-10 16:46:38 +02:00
Miroslav Lichvar
eea343b93f refclock: improve error messages 2018-08-09 14:52:08 +02:00
Miroslav Lichvar
afff06c88c ntp: add options to set minsamples/maxsamples of hwclock 2018-08-09 14:52:08 +02:00
Miroslav Lichvar
c0717a27f6 hwclock: add parameters for minimum/maximum number of samples
Allocate the arrays which hold the samples dynamically and limit the
number of dropped samples to not fall below the minimum.
2018-08-09 14:52:08 +02:00
Miroslav Lichvar
159bd73f76 test: add 127-filter test 2018-08-09 14:52:08 +02:00
Miroslav Lichvar
9931a9166b cmdmon: include filter length in ADD_SERVER/ADD_PEER request 2018-08-09 14:33:48 +02:00
Miroslav Lichvar
8aa4ae027b ntp: add assertion to get_seperation() 2018-08-09 14:33:48 +02:00
Miroslav Lichvar
dcce79fdbe ntp: shorten minimum allowed polling interval
With the filter option it is useful to collect NTP measurements at
a higher rate.
2018-08-09 14:33:48 +02:00
Miroslav Lichvar
189aafde9d ntp: add filter option
Add an option to use the median filter to reduce noise in measurements
before they are accumulated to sourcestats, similarly to reference
clocks. The option specifies how many samples are reduced to a single
sample.

The filter is intended to be used with very short polling intervals in
local networks where it is acceptable to generate a lot of NTP traffic.
2018-08-09 14:33:47 +02:00
Miroslav Lichvar
99e3045df4 ntp: enable auto burst with very short polling intervals
This fixes commit 5b75d4afef.
2018-08-08 11:36:06 +02:00
Miroslav Lichvar
c498c21fad refclock: split off median filter
Move the implementation of the median filter to a separate file to make
it useful for NTP. Replace some constants with parameters and generalize
the code to work with full NTP samples (including root dispersion/delay,
stratum, and leap).

For refclocks it should give the same results as before.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
6bef8aa0e9 use common structure for NTP samples
Define a structure for NTP samples and use it to pass samples from
the ntp_core and refclock code to sources and sourcestats.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
108d112272 sourcestats: don't save stratum for all samples
Save stratum only from the last accumulated sample as only that is
currently needed.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
05078e4252 sourcestats: track leap status
This moves the leap status of the last sample from the source instance
to the sourcestats instance in order to make them both accumulate the
same data.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
4ceb9e4cd0 sys_linux: allow fcntl(F_SETFL) in seccomp filter
This fixes commit 76bed76289.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
a9f237a395 configure: fix detection of timepps.h on FreeBSD
The header requires inttypes.h to be useful.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
e7ca560c3d configure: drop detection of stdint.h and inttypes.h
The current code uses macros from inttypes.h. There is no point in
detecting and selecting between stdint.h and inttypes.h as the latter is
always needed.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
d9f86f6f70 memory: add missing include 2018-08-03 17:21:02 +02:00
Miroslav Lichvar
879d936277 util: handle or ignore SIGPIPE signal
In chronyc handle SIGPIPE similarly to SIGTERM. In chronyd ignore the
signal to avoid crashing when a TCP socket will be needed (e.g. for
NTS-KE) and will be unexpectedly closed from the other side.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
5bb2bf9361 util: handle errors in setting of signal handler as fatal 2018-08-03 17:21:02 +02:00
Miroslav Lichvar
a8167b7959 sched: allow file handler with multiple events to remove itself
Before dispatching a handler, check if it is still valid. This allows a
handler to remove itself when a descriptor has two different events at
the same time.
2018-08-03 17:21:02 +02:00
Miroslav Lichvar
b33b682356 doc: update chrony.conf man page for recent changes 2018-06-22 12:12:11 +02:00
Miroslav Lichvar
2c47602c33 ntp: allow sub-second maxpoll
Remove the maxpoll-specific limit and allow both minpoll and maxpoll to
be set to a negative value.
2018-06-22 12:12:11 +02:00
Miroslav Lichvar
59d1b41716 ntp: restrict use of sub-second polling intervals
When the local polling interval is adjusted between minpoll and maxpoll
to a sub-second value, check if the source is reachable and the minimum
measured delay is 10 milliseconds or less. If it's not, ignore the
maxpoll value and set the interval to 1 second.

This should prevent clients (mis)configured with an extremely short
minpoll/maxpoll from flooding servers on the Internet.
2018-06-22 12:11:36 +02:00
Miroslav Lichvar
5b75d4afef ntp: make burst interval proportional to polling interval
If the polling interval is shorter than 8 seconds, set the burst
interval to the 1/4th of the polling interval instead of the 2-second
constant. This should make the burst option and command useful with
very short polling intervals.
2018-06-22 10:57:03 +02:00
Miroslav Lichvar
e15c7cd236 refclock_sock: downgrade error messages to debug messages
Turn error messages which are not expected to happen with a correctly
working SOCK client into debug messages.
2018-06-20 16:57:24 +02:00
Miroslav Lichvar
9bc774d6af fix printf()/scanf() format signedness
Fix mismatches between the format and sign of variables passed to
printf() or scanf(), which were found in a Frama-C analysis and gcc
using the -Wformat-signedness option.
2018-06-20 16:57:04 +02:00
Miroslav Lichvar
9b34556952 keys: initialize data used for measuring authentication delay
This issue was found in a Frama-C analysis.
2018-06-20 16:45:26 +02:00
Miroslav Lichvar
9a6369d8f1 reference: specify recipient in message when sending email
Instead of adding the recipient to the sendmail command line (which is
interpretted by the shell) add a "To" line to the message and run
sendmail with the -t option to read the recipient from the message.
2018-06-20 16:45:26 +02:00
Miroslav Lichvar
49cdd6bf09 reference: terminate string returned by gethostname()
POSIX doesn't require the string to be terminated if it didn't fit in
the buffer.

This issue was found in a Frama-C analysis.
2018-06-20 16:45:21 +02:00
Miroslav Lichvar
63fe34e890 check values returned by gmtime() and localtime()
While it is not expected to happen with any time that can be represented
by the system clock, the functions are allowed to return NULL. Check the
pointer before dereferencing.

This issue was found in a Frama-C analysis.
2018-06-20 16:45:14 +02:00
Lonnie Abelbeck
85465afb62 client: re-work tab-completion to work with libedit 20180525-3.1
Remove spaces from tab-completion results and now break on a space.
Tested with both readline and editline (libedit)
Incorporated Miroslav's suggestions.
2018-06-18 12:30:45 +02:00
Miroslav Lichvar
339cb06a49 doc: fix description of pidfile directive to mention -Q option
This fixes commit 778fce4039.
2018-06-08 16:44:53 +02:00
Miroslav Lichvar
10150bfcab examples: update pidfile in chronyd.service 2018-06-08 16:44:53 +02:00
Miroslav Lichvar
e50dc739d8 configure: move default pidfile to /var/run/chrony
This allows chronyd to remove its pidfile on exit after dropping the
root privileges in order to prevent another chronyd instance from
failing to start, e.g. due to a wrong SELinux label from chronyd -q.
2018-06-08 16:44:53 +02:00
Miroslav Lichvar
26e08abe71 main: create directories before writing pidfile
This makes it possible to save pidfile in /var/run/chrony.
2018-06-08 16:42:49 +02:00
Miroslav Lichvar
7637faa0d0 ntp: change auto_offline to trigger on failed transmissions
Instead of counting missing responses, switch to the offline state
immediately when sendmsg() fails.

This makes the option usable with servers and networks that may drop
packets, and the effect will be consistent with the onoffline command.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
8a57a28177 examples: update NetworkManager dispatcher script
Replace most of the code with the new onoffline command.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
34db671b57 cmdmon: add onoffline command
The onoffline command tells chronyd to switch all sources to the online
or offline status according to the current network configuration. A
source is considered online if it is possible to send requests to it,
i.e. a route to the network is present.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
8b9021bf34 ntp: allow online/offline state to be selected by connectability
Allow SRC_MAYBE_ONLINE to be specified for new NTP sources and
connectivity setting to select between SRC_ONLINE and SRC_OFFLINE
according to the result of the connect() system call, i.e. check whether
the client has a route to send its requests.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
ce6b896948 ntp: refactor switching between online and offline state
Use an enum to describe connectivity of a source and merge
the NCR and NSR TakeSourceOnline/Offline() functions into
SetConnectivity() functions.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
2962fc6286 ntp: check PHC index before opening device
Apparently, it is possible for an interface to report all necessary
flags for HW timestamping without having a PHC. Check the PHC index to
avoid an error message in the system log saying that /dev/ptp-1 cannot
be opened.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
76bed76289 ntp: enable non-blocking mode on server sockets
Avoid blocking in sendmsg() due to a full send buffer.
2018-05-25 10:53:21 +02:00
Miroslav Lichvar
113f2ebec0 doc: add new questions to FAQ 2018-05-25 10:53:21 +02:00
Miroslav Lichvar
7c5bd948bb util: fall back to reading /dev/urandom when getrandom() blocks
With recent changes in the Linux kernel, the getrandom() system call may
block for a long time after boot on machines that don't have enough
entropy. It blocks the chronyd's initialization before it can detach
from the terminal and may cause a chronyd service to fail to start due
to a timeout.

At least for now, enable the GRND_NONBLOCK flag to make the system call
non-blocking and let the code fall back to reading /dev/urandom (which
never blocks) if the system call failed with EAGAIN or any other error.

This makes the start of chronyd non-deterministic with respect to files
that it needs to open and possibly also makes it slightly easier to
guess the transmit/receive timestamp in client requests until the
urandom source is fully initialized.
2018-05-25 10:53:21 +02:00
Christian Ehrhardt
8cbc68f28f examples: make nm-dispatcher script usable for networkd-dispatcher
Historically there were plenty of callback based implementations around
ifupdown via /etc/network/if-up and similar. NetworkManager added the
dispatcher [1] feature for such a kind of functionality.

But so far a systemd-networkd (only) systemd had no means to handle those
cases. This is solved by networkd-dispatcher which is currently available
at least in ArchLinux and Ubuntu.
It takes away the responsibility to listen on netlink events in each
application and provides a more classic script-drop-in interface to respond
to networkd events [3].

This commit makes the NM example compatible to be used by NetworkManager
dispatcher as well as by networkd-dispatcher. That way we avoid too much
code duplication and can from now on handle special cases in the
beginning so that the tail can stay commonly used.

After discussion on IRC the current check differs by checking the
argument count (only in NetworkManager), if ever needed we could extend
that to check for known custom environment vars (NetworkManager =>
CONNECTION_UUID; networkd-dispatcher => OperationalState).

[1]: https://developer.gnome.org/NetworkManager/stable/NetworkManager.html
[2]: https://github.com/craftyguy/networkd-dispatcher
[3]: https://github.com/systemd/systemd/blob/master/src/systemd/sd-network.h#L86

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2018-04-18 15:55:07 +02:00
Miroslav Lichvar
bf7aa52394 sys_linux: fix building with old libcap versions
The cap_get_bound() function and CAP_IS_SUPPORTED macro were added in
libcap-2.21. Check if the macro is defined before use.

The sys/capability.h header from libcap-2.16 and earlier disables the
linux/types.h header, which breaks the linux/ptp_clock.h header. Change
the order to include sys/capability.h as the last system header.
2018-04-05 16:18:23 +02:00
Miroslav Lichvar
366345790d doc: update NEWS 2018-04-04 09:18:44 +02:00
Miroslav Lichvar
f881c153bf client: update copyright years 2018-04-04 09:18:44 +02:00
Miroslav Lichvar
19f3ab2225 ntp: fix handling of socket errors with error queue
In the next Linux version the recvmmsg() system call will be probably
fixed to not return socket errors (e.g. due to ICMP) when reading from
the error queue.

The NTP I/O code assumed this was the correct behavior. When the system
call is fixed, a socket error on a client socket will cause chronyd to
enter a busy loop consuming the CPU until the receive timeout is reached
(8 seconds by default).

Use getsockopt(SO_ERROR) to clear the socket error when reading from the
error queue failed.
2018-04-04 09:18:44 +02:00
Miroslav Lichvar
fd1e80802f privops: allow binding to acquisition port
Fix the privileged helper process to allow binding of client sockets to
a non-zero acquisition port which is not equal to the server port.
2018-04-04 09:18:34 +02:00
Miroslav Lichvar
4b7cb161a8 doc: improve FAQ 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
7848794222 doc: improve description of fallbackdrift directive 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
94822d5156 doc: improve description of key option in chrony.conf man page 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
e3f840aae9 doc: update installation document 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
5aae563277 update copyright years 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
02de782fa3 ntp: fix compiler warnings
Warnings about using uninitialized variables were seen with gcc-7.3.1
and -O3 in CFLAGS.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
3f6df33feb configure: include SECHASH in chronyc features
chronyc is linked with the crypto library and the keygen command checks
if the specified hash function is known.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
a94f5fe007 test: extend 126-burst 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
63f0234748 test: fix hash unit test for NSS
Some hash functions in the freebl3 library ignore the length of the
output buffer and always return the length of the digest.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
47921c7c0c hash: initialize return value before calling NSS hash functions
Some hash functions in the freebl3 library don't support truncated
digests and either return immediately with no update of the output
length, or ignore the length of the output buffer and always write whole
digest.

Initialize the return value to zero to get correct result with the
former.

This is triggered only in the hash unit test. chronyd always provides a
sufficient buffer for the digest.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
42a85f685e doc: update NEWS 2018-03-15 09:00:47 +01:00
Miroslav Lichvar
feca2399e4 hash: add support for older nettle versions
Use nettle_hashes[] instead of nettle_get_hashes(), which is available
only in nettle >= 3.4. nettle_hashes[] is a symbol available in older
versions and may be renamed in future. In nettle >= 3.4 it is a macro
using nettle_get_hashes() for compatibility.
2018-03-15 09:00:09 +01:00
Miroslav Lichvar
d34e611ec8 doc: update README 2018-03-14 16:14:56 +01:00
Christian Ehrhardt
02098ed830 sys_linux: report if CAP_SYS_TIME is not present
Instead of having adjtimex just fail with a permission issue
improve the error messaging by warning for the lack of
CAP_SYS_TIME on SYS_Linux_Initialise.

Message will look like (instead of only the latter message):
 CAP_SYS_TIME not present
 adjtimex(0x8001) failed : Operation not permitted

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2018-03-14 16:14:56 +01:00
Miroslav Lichvar
aa4228bf1b doc: improve description of -x in chronyd man page 2018-03-14 14:59:41 +01:00
Miroslav Lichvar
b296441708 ntp: fix adjustment of init_local_rx timestamp
This fixes commit f0f18a02a7.
2018-03-14 14:59:41 +01:00
Miroslav Lichvar
b827475378 ntp: add debug message to update of link speed 2018-03-14 14:59:41 +01:00
Miroslav Lichvar
78a6698ae1 test: update compilation test to disable nettle 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
e7b6feb34b doc: update supported hash functions in chrony.conf man apge 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
84be834385 hash: add support for nettle 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
e83d808dfd hash: add support for SHA-3 with libtomcrypt 2018-03-13 10:44:24 +01:00
Miroslav Lichvar
35a68d5b59 test: add hash unit test 2018-03-13 10:44:24 +01:00
Miroslav Lichvar
3c593137b0 doc: fix typo in chrony.conf man page 2018-03-12 12:42:05 +01:00
Miroslav Lichvar
deaf0ffed3 ntp: add missing breaks in switch statement
Fortunately, they didn't change the behavior of the code.
2018-03-12 12:42:05 +01:00
Miroslav Lichvar
af145e871e test: use random version in ntp_core unit test 2018-03-07 13:17:55 +01:00
Miroslav Lichvar
fbca570d0b ntp: respond to NTPv1 client requests with zero mode
NTPv1 packets have a reserved field instead of the mode field and the
actual mode is determined from the port numbers. It seems there is still
a large number of clients sending NTPv1 requests with a zero value in
the field (per RFC 1059).

Follow ntpd and respond to the requests with server mode packets.
2018-03-07 11:36:11 +01:00
Miroslav Lichvar
448ef779c2 client: improve handling of unknown responses
Rework the code to not ignore valid packets with unknown or obsolete
responses and return immediately with "bad reply from daemon" instead of
timing out with "cannot talk to daemon".
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
499a69e611 cmdmon: initialize all bytes of reply buffer
Instead of zeroing individual fields, zero all bytes of the buffer
before the reply is formed.

This may have a small impact on performance, but it simplifies the code
and minimizes the risk of leaking uninitialized memory.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
58c2915878 cmdmon: update protocol changelog 2018-03-06 13:47:25 +01:00
Miroslav Lichvar
eda4b111d3 cmdmon: make length of manual list constant
Make the length of responses containing manual samples constant to
simplify the protocol. It was the only type of response that had a
variable length.

This reverts commit 2343e7a89c.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
c6dd749687 ntp: check RX and TX timestamp in interleaved client requests
Clients sending packets in the interleaved mode are supposed to use
a different receive and transmit timestamp in order to reliably detect
the mode of the response. If an interleaved request with the receive
timestamp equal to the transmit timestamp is detected, respond in the
basic mode.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
d2a96f5fbc doc: update README 2018-03-02 14:46:08 +01:00
Miroslav Lichvar
499f513d40 cmdmon: add shutdown command
The command is functionally equivalent to sending the process the
SIGTERM signal.
2018-03-02 13:04:14 +01:00
Miroslav Lichvar
8b1f68b1b4 ntp: delay enabling permanent kernel RX timestamping on Linux
Wait until a kernel RX timestamp is actually missing before opening the
dummy socket in order to avoid a small performance impact in case the
servers are so slow/distant that the kernel can constantly win the race.
2018-03-02 13:03:26 +01:00
Miroslav Lichvar
8e4c776900 test: add 126-burst test 2018-02-28 10:09:47 +01:00
Miroslav Lichvar
d0eb9427c2 ntp: add burst option
When the burst option is specified in the server/pool directive and the
current poll is longer than the minimum poll, initiate on each poll a
burst with 1 good sample and 2 or 4 total samples according to the
difference between the current and minimum poll.
2018-02-28 10:09:47 +01:00
Miroslav Lichvar
7d100b89fc doc: improve description of server options 2018-02-28 10:08:58 +01:00
Miroslav Lichvar
a4bd7f1800 test: make 119-smoothtime more reliable 2018-02-26 17:24:08 +01:00
Miroslav Lichvar
5308e0a25f sources: include maxclockerror in source selection
In the source selection algorithm, include extra dispersion due to
maxclockerror in the root distance of sources that don't have new
samples (the last sample is older than span of all samples) to not
prefer unreachable sources with a short distance and small skew over
reachable sources for too long, and also to decrease their chances of
becoming falsetickers.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
da862158bf main: open /dev/null as stdin/out/err in daemonization
chronyd doesn't normally write anything to stdout or stderr when running
as a daemon, but it is a good practice to replace them with descriptors
of /dev/null to prevent accidental writes to other files or sockets that
would otherwise take their place.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
7b98443a13 logging: don't write fatal messages to invalid descriptor
If opening the log file specified with the -l option failed (after
closing all descriptors), the error message is written to an invalid
descriptor as no log file or syslog is opened yet. Fix the code to track
when the output is usable.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
4da9f74d24 util: replace assert for missing MD5 with fatal log message
Apparently, on some systems the MD5 function is missing with the NSS
support (freebl3). Instead of failing an assertion, exit with a log
message.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
e41042e258 test: update util unit test 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
5581466c63 test: improve and extend ntp_core unit test 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
e79a6c2116 sourcestats: limit minimum value of std_dev 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
666ece122e ntp: compare receive timestamp when checking for duplicate
Compare both receive and transmit timestamps in the NTP test number 1.

This prevents a client from dropping a valid response in the interleaved
mode if it follows a response in the basic mode and the server did not
have a kernel/hardware transmit timestamp, and the random bits of the
two timestamps happen to be the same (chance of 1 in 2^(32-precision)).
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
2c7ab98370 ntp: don't send packets with RX/TX timestamp equal to another timestamp
Before sending a new packet, check if the receive/transmit timestamp
is not equal to the origin timestamp or the previous receive/transmit
timestamp in order to prevent the packet from being its own valid
response (in the symmetric mode) and invalidate responses to the
previous packet.

This improves protection against replay attacks in the symmetric mode.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
f0f18a02a7 ntp: separate timestamps for restarting symmetric protocol
Save the local receive and remote transmit timestamp needed for
(re)starting the symmetric protocol when no valid reply was received
separately from the timestamps that are used for synchronization of the
local clock.

This extends the interval in which the local NTP state is (partially)
protected against replay attacks in order to complete a measurement
in the interleaved symmetric mode from [last valid RX, next TX] to
[last TX, next TX], i.e. it should be the same as in the basic mode.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
c5d8af0285 main: create directories before refclock initialization
This allows the SOCK refclock to open sockets in the /var/run/chrony
directory.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0ce15a8472 main: improve error message for failed getpwnam() 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
da60629201 configure: improve check for timestamping options
The socket.h header provided by musl doesn't seem to include the kernel
headers and is missing SCM_TIMESTAMPING_PKTINFO, which causes the
Linux-specific code in chrony to fail to build.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
2343e7a89c pktlength: handle truncated MANUAL_LIST reply
Before reading the n_samples field of the MANUAL_LIST reply, check if it
is actually contained in the received message. This does not change the
outcome of the client's length check as the returned length was always
larger than the length of the truncated reply and it was dropped anyway,
but it prevents the client from reading uninitialized memory.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
45f27f4f5e sourcestats: reset instance before loading dump file
Don't rely on the caller to reset the instance and always reset it
before loading data to make sure it can't get to an unexpected state.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0bc112f8b4 doc: improve description of refclock tai option
Emphasize that tzdata must be kept up to date in order for the
correction to work as expected.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
bfc2fa645c test: add 125-packetloss test 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
11111804fd test: extend 106-refclock 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
87ec67247e test: extend 110-chronyc 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0df8328ceb ntp: keep kernel RX timestamping permanently enabled on Linux
The Linux kernel has a counter for sockets using kernel RX timestamping
and timestamps (all) received packets only when it is not zero. However,
this counter is updated asynchronously from setsockopt(). If there are
currently no other sockets using the timestamping, it is possible that a
fast server response is received before the kernel timestamping is
actually enabled after setting the socket option and sending a request.

Open a dummy socket on start to make sure there is always at least one
timestamping socket to avoid the race condition.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
b563048ee2 examples: ignore non-up/down events in nm-dispatcher script 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
e8096330be sys_linux: don't keep CAP_SYS_TIME with -x option
When dropping the root privileges, don't try to keep the CAP_SYS_TIME
capability if the -x option was enabled. This allows chronyd to be
started without the capability (e.g. in containers) and also drop the
root privileges.
2018-02-05 14:05:19 +01:00
Miroslav Lichvar
b1647dbcb7 ntp: wait for late HW TX timestamps
When sending client requests to a close and fast server, it is possible
that a response will be received before the HW transmit timestamp of
the request itself. To avoid processing of the response without the HW
timestamp, monitor events returned by select() and suspend reading of
packets from the receive queue for up to 200 microseconds. As the
requests are normally separated by at least 200 milliseconds, it is
sufficient to monitor and suspend one socket at a time.
2018-02-02 11:36:38 +01:00
Miroslav Lichvar
4ddadd5622 ntp: don't request TX timestamp when SW/HW timestamping is disabled 2018-02-01 17:27:45 +01:00
Miroslav Lichvar
3e854006c7 ntp: add missing header guard 2018-01-31 17:23:40 +01:00
Miroslav Lichvar
2c4c235147 sched: allow enabling/disabling individual file handler events 2018-01-30 15:56:51 +01:00
Miroslav Lichvar
6863e43269 client: avoid reading clock after sending request
If chronyc sent a request which caused chronyd to step the clock (e.g.
makestep, settime) and the second reading of the clock before calling
select() to wait for a response happened after the clock was stepped, a
new request could be sent immediately and chronyd would process the same
command twice. If the second request failed (e.g. a settime request too
close to the first request), chronyc would report an error.

Change the submit_request() function to read the clock only once per
select() to wait for the first response even when the clock was stepped.
2017-12-12 11:37:36 +01:00
Miroslav Lichvar
de8708f331 client: remove unused file descriptor sets 2017-12-05 12:13:40 +01:00
Miroslav Lichvar
d0b2486036 client: don't call select() with invalid timeout
If the system clock was stepped forward after chronyc sent a request and
before it read the clock in order to calculate the receive timeout,
select() could be called with a negative timeout, which resulted in an
infinite loop waiting for select() to succeed.

Fix the submit_request() function to not call select() with a negative
timeout. Also, return immediately on any error of select().
2017-12-05 12:13:37 +01:00
Miroslav Lichvar
5384a93645 test: extend util unit test 2017-12-05 10:14:19 +01:00
Miroslav Lichvar
4bbc768652 util: avoid casting to long in UTI_DoubleToTimeval() 2017-12-05 09:44:59 +01:00
Vincent Blut
fead915b45 doc: fix typo in chronyd man page 2017-12-04 11:16:20 +01:00
Miroslav Lichvar
5422e49026 doc: improve leapsectz description 2017-10-12 14:07:12 +02:00
Miroslav Lichvar
77a1f27a1d test: add 124-tai test 2017-10-11 17:49:30 +02:00
Miroslav Lichvar
b45d864f73 test: check for maxchange message in check_chronyd_exit() 2017-10-11 17:49:21 +02:00
Miroslav Lichvar
f35c81c871 refclock: improve TAI-UTC conversion
Instead of using the TAI-UTC offset which corresponds to the current
system time, get the offset for the reference time. This allows the
clock to be accurately stepped from a time with different TAI-UTC
offset.
2017-10-11 17:45:21 +02:00
Miroslav Lichvar
a349b2803c refclock: remove unnecessary return statements 2017-10-11 17:28:34 +02:00
Chris Perl
f5d1b8fb74 refclock: add tai option
This option is for indicating to chronyd that the reference clock is
kept in TAI and that chrony should attempt to convert from TAI to UTC by
using the timezone configured by the "leapsectz" directive.
2017-10-11 17:28:34 +02:00
Chris Perl
a0fe71eef1 reference: add function to get TAI-UTC offset 2017-10-11 17:28:34 +02:00
Andreas Steinmetz
154b39cf7a refclock: add stratum option 2017-10-09 10:39:20 +02:00
Bernhard M. Wiedemann
6f54210db2 configure: allow to override build date
in order to make builds reproducible.
See https://reproducible-builds.org/ for why this is good
and https://reproducible-builds.org/specs/source-date-epoch/
for the definition of this variable.
2017-10-05 18:18:07 +02:00
Miroslav Lichvar
f6539449c5 nameserv: set hints for getaddrinfo() according to -4/-6 option
Avoid sending unnecessary DNS requests when the -4/-6 option is
specified.
2017-10-04 11:20:10 +02:00
Miroslav Lichvar
b8d546a0d1 examples: add leapsectz to configuration examples 2017-09-15 08:32:09 +02:00
Miroslav Lichvar
04e6474b75 reference: check for gmtime() error
Although gmtime() is expected to convert any time of the system clock at
least in the next few NTP eras, a correct code should always check the
returned value and this shouldn't be a fatal error in handling of leap
seconds.
2017-09-15 08:32:08 +02:00
Vincent Blut
eb51c500e8 doc: fix typo in chrony.conf man page 2017-09-11 11:21:13 +02:00
Miroslav Lichvar
6f8fba9a3f conf: check if GLOB_NOMAGIC is defined
This option is not supported by musl and possibly other libc
implementations.
2017-09-01 11:32:16 +02:00
Miroslav Lichvar
750afc30f2 test: fix keys unit test 2017-09-01 11:28:55 +02:00
Miroslav Lichvar
e0e6ec0d84 doc: update NEWS 2017-08-29 14:17:35 +02:00
Miroslav Lichvar
c9f50fc686 update copyright years 2017-08-28 14:38:23 +02:00
Miroslav Lichvar
83c26b458b doc: fix spelling
Don't mix UK and US spelling.
2017-08-28 14:38:19 +02:00
Miroslav Lichvar
b711873f45 test: add 123-mindelay test 2017-08-28 14:27:14 +02:00
Miroslav Lichvar
c68ca40ce4 ntp: improve maxdelayratio test
Similarly to the maxdelaydevratio test, include in the maximum delay
dispersion which accumulated in the interval since the last sample.
Also, enable the test for symmetric associations.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
51fe80ad95 sourcestats: move maxdelaydevratio test to ntp_core
Instead of giving NTP-specific data to sourcestats in order to perform
the test, provide a function to get all data needed for the test in
ntp_core. While at it, improve the naming of variables.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
7ffee73524 memory: check for overflow when (re)allocating array
When (re)allocating an array with very large number of elements using
the MallocArray or ReallocArray macros, the calculated size of the array
could overflow size_t and less memory would be allocated than requested.

Add new functions for (re)allocating arrays that check the size and use
them in the MallocArray and ReallocArray macros.

This couldn't be exploited, because all arrays that can grow with cmdmon
or NTP requests already have their size checked before allocation, or
they are much smaller than memory allocated for structures to which they
are related (i.e. ntp_core and sourcestats instances), so a memory
allocation would fail before their size could overflow.

This issue was found in an audit performed by Cure53 and sponsored by
Mozilla.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
f40b0024bd util: check for gmtime() error
Fix the UTI_TimeToLogForm() function to check if gmtime() didn't fail.
This caused chronyc to crash due to dereferencing a NULL pointer when
a response to the "manual list" request contained time which gmtime()
could not convert to broken-down representation.

This issue was found in an audit performed by Cure53 and sponsored by
Mozilla.
2017-08-28 14:27:14 +02:00
Miroslav Lichvar
a06c9909a6 conf: use enum for RX filter 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
aee42fada8 ntp: allow TX-only HW timestamping by default
If no rxfilter is specified in the hwtimestamp directive and the NIC
doesn't support the all or ntp filter, enable TX-only HW timestamping
with the none filter.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
3e93068c43 hwclock: improve debug message 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
36291b707b hwclock: check if estimated frequency is sane 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
6dad2c24bf hwclock: drop all samples on reset
On some HW it seems it's possible to get an occasional bad reading of
the PHC (with normal delay), or in a worse case the clock can step due
to a HW/driver bug, which triggers reset of the HW clock instance. To
avoid having a bad estimate of the frequency when the next (good) sample
is accumulated, drop also the last sample which triggered the reset.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
27cbf20d23 doc: include uncorrected offset in bound on maximum error 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
5c571bbbe7 reference: add new fields to tracking log
Add the root delay, root dispersion and maximum estimated error in the
interval since the previous update to the tracking log.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
33d65c8614 reference: separate calculation of root dispersion 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
d87db7cdb8 reference: refactor log writing
Remove unnecessary parameters of the write_log() function.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
45fa4750da reference: don't update fallback drift on manual input
This fixes a crash due to assertion failure in update_fb_drifts() when
fallbackdrift is enabled and manual input is provided.
2017-08-23 15:01:30 +02:00
Miroslav Lichvar
8472fd8133 reference: simplify check for NaN 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
5ab645e310 cmdmon: add new fields to ADD_SERVER/ADD_PEER request 2017-08-23 15:01:30 +02:00
Miroslav Lichvar
8ccda538d3 conf: add mindelay and asymmetry options to NTP sources 2017-08-23 15:01:28 +02:00
Miroslav Lichvar
b06d74ab73 sourcestats: add fixed asymmetry
Rework the code to allow the jitter asymmetry to be specified.
2017-08-23 14:33:23 +02:00
Miroslav Lichvar
d0964ffa83 sourcestats: add fixed minimum delay
If the minimum delay is known (in a static network configuration), it
can replace the measured minimum from the register. This should improve
the stability of corrections for asymmetric jitter, sample weighting and
maxdelay* tests.
2017-08-23 14:14:06 +02:00
Miroslav Lichvar
3d08815efb sys_linux: fix building with older kernel headers
Programming pins for external PHC timestamping was added in Linux 3.15,
but the PHC subsystem is older than that. Compile the programming code
only when the ioctl is defined.
2017-08-15 13:39:39 +02:00
Miroslav Lichvar
a83f0d3cdc util: simplify clamping in UTI_TimespecNetworkToHost()
This should fix a coverity warning.
2017-08-15 13:27:50 +02:00
Miroslav Lichvar
702db726d3 util: add assertion for NTP timestamp size 2017-08-15 13:27:50 +02:00
Miroslav Lichvar
ed5c43204b smooth: don't adjust invalid time of last update 2017-08-15 13:27:50 +02:00
Miroslav Lichvar
f91bdd604d reference: don't adjust invalid reference time 2017-08-15 13:27:50 +02:00
Miroslav Lichvar
3a1dbb1354 test: fix ntp_core unit test
This fixes commit b896bb5a78.
2017-08-09 10:41:30 +02:00
Bryan Christianson
4b511143b8 sys_netbsd: fix adjtime() fault on macOS
On some systems, passing NULL as the first argument to adjtime, will
result in returning the amount of adjustment outstanding from a previous
call to adjtime().

On macOS this is not allowed and the adjtime call will fault. We can
simulate the behaviour of the other systems by cancelling the current
adjustment then restarting the adjustment using the outstanding time
that was returned. On macOS 10.13 and later, the netbsd driver is now
used and must use these semantics when making/measuring corrections.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
93076e7e1c client: fix parsing of -v command option
The sources and sourcestats commands accept -v as an option, but the
glibc implementation of getopt() reorders the arguments and parses the
option as a command-line option of chronyc.

Add '+' to the getopt string to disable this feature. Other getopt()
implementations should consider it a new command-line option, which will
be handled as an error if present.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
1c51feb3c5 sched: add new timeout class for peer transmissions
This allows transmissions in symmetric mode to be scheduled
independently from client transmissions. This reduces maximum delay
in scheduling when chronyd is configured with a larger number of
servers.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
c2773dbc2f test: improve hwclock unit test 2017-08-09 09:57:14 +02:00
Miroslav Lichvar
4534db84c4 hwclock: fix conversion of HW timestamps
Fix a sign error in conversion of HW time to local time, which caused
the jitter to be amplified instead of reduced. NTP with HW timestamping
should now be more stable and able to ignore occasionally delayed
readings of PHC.
2017-08-09 09:57:14 +02:00
Miroslav Lichvar
be8215e181 ntp: minimize data in client mode packets
In basic client mode, set the origin and receive timestamp to zero.
This reduces the amount of information useful for fingerprinting and
improves privacy as the origin timestamp allows a passive observer to
track individual NTP clients as they move across networks. (With chrony
clients that assumes the timestamp wasn't reset by the chronyc offline
and online commands.)

This follows recommendations from the current version of IETF draft on
NTP data minimization [1].

The timestamp could be theoretically useful for enhanced rate limiting
which can limit individual clients behind NAT and better deal with DoS
attacks, but no server implementation is known to do that.

[1] https://tools.ietf.org/html/draft-ietf-ntp-data-minimization-01
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
ae82bbbace examples: improve NetworkManager dispatcher script
When no default route is configured, check each source if it has a
route. If the system has multiple network interfaces, this prevents
setting local NTP servers to offline when they can still be reached over
one of the interfaces.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
2b6ea41062 doc: fix server mode number in chrony.conf man page 2017-08-09 09:57:13 +02:00
Miroslav Lichvar
d9f745fe70 doc: update chrony.conf man page for recent changes 2017-08-09 09:57:13 +02:00
Miroslav Lichvar
9aac179367 ntp: skip IPv6 extension headers
Handle IPv6 packets with extension headers received from the error queue
on Linux.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
b896bb5a78 ntp: don't send useless requests in interleaved client mode
In interleaved client mode, when so many consecutive requests were lost
that the first valid (interleaved) response would be dropped for being
too old, switch to basic mode so the response can be accepted if it
doesn't fail in the other tests.

This reworks commit 16afa8eb50.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
64c2fd9888 ntp: limit number of interleaved responses in symmetric mode
In symmetric mode, don't send a packet in interleaved mode unless it is
the first response to the last valid request received from the peer and
there was just one response to the previous valid request. This prevents
the peer from matching the transmit timestamp with an older response if
it can't detect missed responses.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
2668a12e4e ntp: improve detection of missed packets in interleaved mode
In interleaved symmetric mode, check if the remote TX timestamp is
before RX timestamp. Only the first response from the peer after
receiving a request should pass this test. Check also the interval
between last two remote transmit timestamps when we know the remote poll
can't be constrained by minpoll. Use the minimum of previous remote and
local poll as a lower bound of the actual interval between peer's
transmissions.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
e1645966ec ntp: enable maxdelayratio test in interleaved client mode
With more accurate delay in interleaved mode the test should now be as
reliable as in basic mode.
2017-08-09 09:57:13 +02:00
Bryan Christianson
4f1fc1ee78 main: fix -q option
Attempting to step the system clock by using the -q option with chronyd
would fail.
2017-08-09 09:57:13 +02:00
Miroslav Lichvar
d70df3daab logging: enable line buffering of file log
The file log specified with the -l option should have the messages as
soon as they are produced.
2017-08-08 15:37:59 +02:00
Miroslav Lichvar
554b9b06de doc: update NEWS 2017-07-25 17:54:01 +02:00
Miroslav Lichvar
f734bd1a7c sys_linux: allow getrandom in seccomp filter
This fixes commit c5735ebfe9.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
77fc5c42b9 client: don't allow slash with hostname in allow/deny command 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
ea85bc43e0 conf: don't allow slash with hostname in allow/deny directive 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
e8fb11c433 reference: don't report zero stratum when synchronised
If synchronised to a stratum 15 source, return stratum of 16 instead of
0 in the tracking report. It will not match the value in server mode
packets, but it should be less confusing.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
01a29c7a11 cmdmon: report offset after manual timestamp as float
Modify the protocol to report the offset as seconds in floating point
instead of integer number of centiseconds.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
6ec3dc1650 manual: handle failed robust regression 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
0c54cf316d util: avoid undefined behavior in timestamp conversion 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
bd3fb49a1e client: avoid undefined bit shifts 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
f6e72a80e1 regress: avoid undefined behavior in pointer arithmetic 2017-07-21 17:14:15 +02:00
Miroslav Lichvar
c2ab1426e5 ntp: simplify get_poll_adj() 2017-07-21 16:27:03 +02:00
Miroslav Lichvar
fa2c59d78d sourcestats: increase number of samples needed to check delay
Require at least 6 samples to check the increase in the delay of a new
sample to make it more reliable.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
16afa8eb50 ntp: don't accumulate old samples in interleaved client mode
Check how many responses were missing before accumulating a sample using
old timestamps to avoid correcting the clock with an offset extrapolated
over a long interval.

This should be eventually done in sourcestats for all sources.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
992590e99c ntp: revert reversed poll tracking in interleaved mode
With the new selection of timestamps in the interleaved mode it's no
longer necessary to reverse the poll tracking in order to reduce the
local and remote intervals of measurements that makes the peer with
higher stratum.

This reverts commit 4a24368763.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
0baa35eade ntp: select timestamps in interleaved mode
Use previous local TX and remote RX timestamps for the new sample in the
interleaved mode if it will make the local and remote intervals
significantly shorter in order to improve the accuracy of the measured
delay.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
2e0870ee0c ntp: refactor timestamp selection and interval calculation
Prepare the code for a third option in the timestamp selection and clean
it up a bit.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
43cd119d6d ntp: add function for zeroing local timestamps 2017-07-21 16:27:03 +02:00
Miroslav Lichvar
62cd319a51 ntp: fix poll in source report
The source report used the local interval, which in symmetric mode may
be longer than the actual interval used for transmission.
2017-07-14 20:25:50 +02:00
Miroslav Lichvar
d0f789425b ntp: ignore saved remote poll when peer is not responding
When a peer stops responding, allow our actual polling interval to be
longer than poll saved from the last valid response.
2017-07-14 20:06:31 +02:00
Miroslav Lichvar
30e6549692 ntp: reset TX counter on all valid responses
Also change it to an unsigned type.
2017-07-14 19:40:44 +02:00
Miroslav Lichvar
043c7d7c9f configure: fix compiler warning in getrandom() test 2017-07-14 10:21:31 +02:00
Miroslav Lichvar
1c277a8850 configure: check for hardening compiler options
If no CFLAGS are specified, check if common security hardening options
are supported and add them to the CFLAGS/LDFLAGS. These are typically
enabled in downstream packages, but users compiling chrony from sources
with default CFLAGS should get hardened binaries too.
2017-07-13 16:12:25 +02:00
Bryan Christianson
ccb94ac5fb sys_macosx: add support for ntp_adjtime() on macOS 10.13+
macOS 10.13 will implement the ntp_adjtime() system call, allowing
better control over the system clock than is possible with the existing
adjtime() system call. chronyd will support both the older and newer
calls, enabling binary code to run without recompilation on macOS 10.9
through macOS 10.13.

Early releases of macOS 10.13 have a very buggy adjtime() call. The
macOS driver tests adjtime() to see if the bug has been fixed. If the
bug persists then the timex driver is invoked otherwise the netbsd
driver.
2017-07-13 16:10:54 +02:00
Miroslav Lichvar
778fce4039 main: don't require root privileges with -Q option
If the -Q option is specified, disable by default pidfile, ntpport,
cmdport, Unix domain command socket, and clock control, in order to
allow starting chronyd without root privileges and/or when another
chronyd instance is already running.
2017-07-13 16:10:54 +02:00
Miroslav Lichvar
9983185d6d ntp: define NTP port for configuration code 2017-07-13 16:10:54 +02:00
Miroslav Lichvar
7bd1c02781 main: refactor check of pidfile 2017-07-13 16:10:54 +02:00
Miroslav Lichvar
760285218f sys_timex: fix update of TAI offset on non-Linux systems
The tai field in struct timex is a Linux-specific feature. It's possible
to read the current offset with ntp_gettime() (or ntp_gettimex() on
Linux), but apparently not all libc implementations support it.

Rework the code to save and adjust the last value instead of reading
the current value from the kernel.
2017-07-11 11:28:34 +02:00
Miroslav Lichvar
4fe0e6b7fd sys_timex: rename status variable 2017-07-10 14:48:47 +02:00
Miroslav Lichvar
0773a1e630 ntp: fix debug message about unknown HW timestamping ifindex 2017-06-30 17:01:06 +02:00
Miroslav Lichvar
4a24368763 ntp: reverse poll tracking in interleaved symmetric mode
Unlike in the basic mode, the peer with a higher stratum needs to wait
for a response before sending the next request in order to minimize the
delay of the measurement and error in the measured delay.

Slightly increase the delay adjustment to make it work with older chrony
versions.
2017-06-30 17:01:06 +02:00
Miroslav Lichvar
577290c5bc ntp: fix poll interleaving with unsynchronised peers
Update the remote poll and remote stratum even for unsychronised peers,
and handle stratum of 0 as 16, so the peers work with the opposite
differences between their strata and can adjust their polling intervals
in order to interleave the packets.
2017-06-30 17:01:01 +02:00
Miroslav Lichvar
854ff69f78 hwclock: decrease tolerance of robust regression to 0.1 ppb 2017-06-30 16:58:57 +02:00
Miroslav Lichvar
29b0ad894c reference: get TAI-UTC offset from leap second timezone
Use the timezone specified by the leapsectz directive to get the
current TAI-UTC offset and set the offset of the system clock in order
to provide correct TAI time to applications using ntp_adjtime(),
ntp_gettime(), or clock_gettime(CLOCK_TAI).
2017-06-30 16:58:53 +02:00
Miroslav Lichvar
cde0a20307 sys_timex: add support for setting TAI-UTC offset 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
a768578a26 local: add support for setting TAI-UTC offset 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
5d838729ef reference: move static tz variables to function using them 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
d6b763dc24 client: check IP address family before printing as refid 2017-06-30 13:43:27 +02:00
Miroslav Lichvar
95adb52a45 configure: add missing object for PHC refclock
This fixes commit eceb8d9937.
2017-06-27 15:29:02 +02:00
Miroslav Lichvar
707d9a3484 test: add regress unit test 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
1872d4d195 test: fix crash when printing debug messages
This fixes commit 6cbeb107db.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
17f32c266e sourcestats: use median distance in weight calculation
Replace mean distance with median distance in the weight calculation.
This should make the weights less sensitive to outliers.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
6207655ab2 regress: provide function to find median 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
5e1e31ad5f regress: reduce maximum number of points to 64
This corresponds to the maximum number of points used by regress users.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
13111c1dd8 regress: use chars instead of ints for flags
This reduces the size of the flags array on stack.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
85c84073c1 regress: fix assertion in robust regression 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
c2944d8727 regress: speed up range expansion in robust regression
Instead of repeatedly expanding the range of b with the same increment,
double the range on each iteration to speed up the expansion. Also, add
a sanity check for the interval.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
e118b9b1e8 regress: fix robust regression
The bisection always terminated after one iteration. Change the code to
check if the middle is different from the lower and upper limits as
suggested in the original recipe.

This fixes commit b14689d59b.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
7fb7f95979 sourcestats: include precision in weight calculation
In order to stabilize the weights of refclock samples which have only
slightly different distances, don't allow the stddev value used in the
weight calculation to be smaller than the precision and also assign
weight of 1 to all samples which have distance < minimum + precision.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
cc507bffae conf: abort when include directive fails
When parsing the include directive, call glob() with the GLOB_ERR and
GLOB_NOMAGIC flags, and abort with an error message when matching of the
pattern failed with other error than GLOB_NOMATCH.

This restores the original behavior of the directive when it didn't
allow patterns, but it will still not fail with patterns not matching
any files in an existing directory.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
0dbfe020ad refclock: set default precision to precision of system clock 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
018a1c42b0 ntp: suggest clients to increase their polling interval
When the poll value in a client request is smaller than the server's NTP
rate limiting interval, set poll in the response to the rate limiting
interval to suggest the client to increase its polling interval.

This follows ntpd as a server. No current client implementation seems to
be increasing its interval by the poll, but it may change in the future.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
c5735ebfe9 util: add support for getrandom()
Add support for the Linux getrandom() system call, which is available
in glibc since 2.25.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
db93180ce1 ntp: apply HW TX/RX compensation to system time
Apply the compensation to the cooked local time instead of HW time. This
might make a difference when the HW clock has a large frequency error.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
39da10d939 doc: update description of hwtimestamp directive 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
f2da253bc3 ntp: add option to select HW RX timestamping filter
Add an rxfilter option to the hwtimestamp directive to select which
received packets should be timestamped. It can be set to "none", "ntp",
or "all". The default value is ntp, which falls back to all when ntp is
not supported.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
934d4047f1 ntp: add support for new Linux timestamping options
New timestamping options may be available in kernel 4.13. They can be
used to get the index of the interface which timestamped incoming packet
together with its length at layer 2, enable simultaneous SW and HW TX
timestamping, and enable a new RX filter for NTP packets.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
b799cfd1c4 ntp: always try to enable SW timestamping on Linux
Request SW timestamps with SCM_TIMESTAMPING even if HW timestamping is
enabled. This replaces SCM_TIMESTAMP(NS) for RX and enables TX SW
timestamping on interfaces that don't support HW timestamping (or don't
have it enabled) if another interface has HW timestamping enabled.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
b712c100d7 main: close logs as last thing before exit
This should prevent losing messages from other finalisation code.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
c049bce007 client: try to connect to all addresses before giving up
Don't give up when one of the addresses/hostnames specified by -h fails
to resolve in DNS_Name2IPAddress(), e.g. with the default setting try to
connect to ::1 even when 127.0.0.1 failed due to the -6 option.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
46fad717e5 client: use getopt() for command line parsing 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
ae0c3bbbe8 main: use getopt() for command line parsing
This allows multiple options to be specified together and also may
options follow configuration directives on systems where getopt()
permutates the arguments.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
f95d57e0d9 doc: fix typo in chronyd man page 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
a1cbd4eb82 main: add option to specify log file
Add -l option to log to a file instead of syslog or terminal.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
6cbeb107db logging: allow logging to file instead of syslog 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
3a5566c6c3 main: use LOG_FATAL to print error when UID is not zero 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
73c548ad01 sourcestats: handle negative elapsed time in SST_GetSelectionData()
Source selection uses the last event time as current time. If it was
called from a refclock which generates a sample in its poll function
(e.g. PHC), the sample time may be later than the event time. This
gives a negative elapsed time in SST_GetSelectionData() and possibly
also a negative root distance, which causes the source to be rejected as
a falseticker.

Use absolute value of the difference in order to always get a positive
root distance.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
82203e12c8 doc: update refclock documentation 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
1ca099473f refclock: add option to filter wrong pulse edges
Add width option to the refclock directive to set expected width of
pulses in a PPS signal. The width adds a limit for the maximum offset
and root distance in order to reject PPS samples from wrong events, e.g.
PHCs which cannot be configured to timestamp only rising of falling
edges.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
eceb8d9937 refclock_phc: add support for timestamping of external PPS
Add extpps driver option to the PHC refclock to enable external
timestamping of PPS signal and also options to configure the channel and
pin index. In this mode, the driver polling function accumulates samples
for hwclock, which is used to convert received timestamping events to
local time.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
4ba92bb6d6 sys_linux: add support for external PHC timestamping 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
f31f68ae8e refclock: add option to treat non-PPS refclocks as PPS
Add pps option to the refclock directive to force chronyd to treat any
refclock as a PPS refclock. This is intended for refclocks that may
provide time off by a whole number of seconds due to missing or wrong
TAI/GPS->UTC conversion.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
cff15f91d4 refclock: allow all drivers to provide PPS samples 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
6b74917954 refclock: allow drivers to provide cooked PPS samples
Split RCL_AddPulse() in order to provide a new function for refclock
drivers which can make PPS samples without having raw system time, e.g.
from PHC timestamps.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
1bf2384a1f refclock: don't require raw time in valid_sample_time()
This makes the check a bit more expensive, but it will be needed to
allow refclocks that don't have raw system time.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
54a12779e2 ntp: include local error in hwclock samples 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
e8b06fef9f ntp: remove unnecessary include 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
653d70ec4e sys_linux: allow sysinfo in seccomp filter
It may be used by glob() in latest glibc.
2017-04-19 14:38:51 +02:00
Miroslav Lichvar
abb09418b1 sys_linux: don't drop PHC samples with zero delay
When processing data from the PTP_SYS_OFFSET ioctl, the sample is
dropped when an interval between two consecutive readings of the system
clock is negative or zero, assuming the clock has been stepped between
the two readings.

With a real PHC the interval is normally expected to be at least a
microsecond, but with a virtual PHC and a low-resolution system clock
it's possible to get two readings with the same system time. Modify the
check to drop only samples with a negative delay.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
c103bebd9f configure: check for clang
Try clang as the C compiler before cc and use the same -W* CFLAGS as
with gcc.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
935d855b47 util: indicate truncated Unix socket path in UTI_SockaddrToString()
Specify the maximum length of the path in the snprintf() format to avoid
a new gcc warning (-Wformat-truncation). If the path doesn't fit in the
buffer, indicate with the '>' symbol that it was truncated. The function
is used only for debug messages.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
f8f9100a0d makefile: run tests in multiple iterations on check
Use the new options of the run script in the check target to make it
reliable for automatic testing without using a fixed random seed and add
a new quickcheck target for the original check using just one iteration.
2017-03-31 14:53:27 +02:00
Miroslav Lichvar
6de7b98e76 test: improve run script
Add options to allow running the tests in multiple iterations while
allowing a small number of failures per test. Some tests are expected to
fail occasionally as they are basically statistical tests. Improving
their reliability is possible, but it's always a compromise between
sensitivity, reliability, and execution time.
2017-03-31 14:53:27 +02:00
Miroslav Lichvar
c390351c65 test: make 118-maxdelay more reliable 2017-03-31 14:53:27 +02:00
Miroslav Lichvar
768bce799b sys_linux: allow getpid in seccomp filter
It seems to be used by syslog() in latest glibc.
2017-03-13 14:42:44 +01:00
Miroslav Lichvar
d3a30142e5 test: fix DEBUG_LOG use in unit tests
This was missing in commit f282856c72.
2017-03-13 12:04:26 +01:00
Chris Perl
3a635fc51f sourcestats: reorder arguments to DEBUG_LOG in SST_IsGoodSample
The delay_increase and allowed_increase variables are backwards with
respect to the ordering of the words in the message.
2017-03-10 16:55:22 +01:00
Miroslav Lichvar
10078566da test: make 117-fallbackdrift more reliable 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
c44346096c sys: add null driver
Add a new clock driver that doesn't actually try to adjust the clock.
It allows chronyd to run without the capability to adjust/set the system
clock, e.g. in some containers. It can be enabled by the -x option.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
0ff449e6a6 local: improve log message for failed clock step 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
f3a16383b9 main: dump history by default
Always write the measurement history on exit when the dump directory is
specified and silently ignore the dumponexit directive. There doesn't
seem to be a good use case for dumpdir and -r without dumponexit as the
history would be invalidated by adjustments of the clock that happened
between the dump command and chronyd exit.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
539ef3f770 main: rewrite some error messages 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
f282856c72 logging: remove facility parameter
It was never used for anything and messages in debug output already
include filenames, which can be easily grepped if there is a need
to see log messages only from a particular file.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
6db8ec1ba2 privops: separate res_init() call
Move the res_init() call from do_name_to_ipaddress() into a separate
privops operation. Use it in ntp_sources and avoid unnecessary
res_init() calls in the main thread.
2017-03-10 16:51:02 +01:00
Miroslav Lichvar
5187c08c90 doc: update NEWS 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
c8076ac10d makefile: fix distclean target to not print errors 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
362d155558 examples: improve configuration examples 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
7b7eb0a6e5 examples: improve systemd unit files
Add the PrivateTmp, ProtectHome, and ProtectSystem directives to better
secure the system from chronyd. It's taken from the Debian chrony
package.
2017-01-31 11:22:11 +01:00
Miroslav Lichvar
d96f49f67d test: add keys unit test 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
43ba5d2126 doc: document rekey in chronyc man page
For some reason this useful command was never documented.
2017-01-31 11:22:11 +01:00
Miroslav Lichvar
48f7598fed client: add rekey to help text 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
510b22e96b util: fix more coverity warnings
Coverity doesn't seem to like the new field in the IPAddr struct (used
as explicit padding of the structure) to be left uninitialized, even
though it's never used for anything and is cleared by memset() in
UTI_IPHostToNetwork() before leaving the process.
2017-01-31 11:22:10 +01:00
Miroslav Lichvar
0a0aff14d8 conf: add rawmeasurements log option
While the measurements log can be useful for debugging problems in NTP
configuration (e.g. authentication failures with symmetric keys), it
seems most users are interested only in valid measurements (e.g. for
producing graphs) and don't expect/handle entries where some of the RFC
5905 tests 1-7 failed. Modify the measurements log option to log only
valid measurements, and for debugging purposes add a new rawmeasurements
option.
2017-01-31 11:22:10 +01:00
Miroslav Lichvar
e225ac68bc test: update 110-chronyc 2017-01-27 11:54:12 +01:00
Miroslav Lichvar
58060c40a5 doc: improve FAQ 2017-01-27 11:45:50 +01:00
Miroslav Lichvar
2ac1b3d5c4 client: print tracking delay/dispersion in nanosecond resolution 2017-01-27 11:35:38 +01:00
Miroslav Lichvar
c174566982 ntp: check supported flags before enabling HW timestamping 2017-01-27 11:35:38 +01:00
Miroslav Lichvar
60fca19d40 ntp: log info message when HW timestamping is enabled 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
8bcb15b02f doc: improve description of some server options 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
65c2cebcd5 reference: report zero root dispersion with local reference
The server's precision is supposed to be included in client's
dispersion. Don't include it in the server's dispersion.
2017-01-27 10:55:28 +01:00
Miroslav Lichvar
2a51b45a43 test: fix memory leaks in unit tests 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
5ac791665e doc: update NEWS 2017-01-24 15:03:24 +01:00
Miroslav Lichvar
a4e3f83611 update copyright years 2017-01-24 15:01:38 +01:00
Miroslav Lichvar
8a837f9c2b test: extend 119-smoothtime 2017-01-23 16:17:39 +01:00
Miroslav Lichvar
da2d33e9a8 ntp: fix time smoothing in interleaved mode
When the server's transmit timestamp was updated with a kernel/HW
timestamp, it didn't include the time smoothing offset. If the offset
was larger than one second, the update failed and clients using the
interleaved mode received less accurate timestamps. If the update
succeeded, the clients received timestamps that were not adjusted for
the time smoothing offset, which added an error of up to 0.5s/1s to
their measured offset/delay.

Fix the update to include the smoothing offset in the new timestamp.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4b98dadae9 ntp: simplify UTI_Ntp64ToTimespec() callers
Since UTI_Ntp64ToTimespec() was modified to handle zero timestamps, some
of its callers don't need to do that anymore.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
86acea5c46 ntp: add interface index to NTP_Local_Address
This will allow us to get the interface index when sending responses to
clients.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
a60fc73e7b refclock_phc: add nocrossts option 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
50f99ec5f4 conf: add nocrossts option to hwtimestamp directive
This option disables the use of the PTP_SYS_OFFSET_PRECISE ioctl.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
31b6a14444 sys_linux: add support for PTP_SYS_OFFSET_PRECISE
This is for hardware that can precisely cross timestamp the PHC with the
system clock.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9df4d36157 refclock_phc: use sys_linux code for reading PHC
This drops support for non-ioctl reading of PHC.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b70f0b674f ntp: move PHC-specific code to sys_linux
This will allow sharing of the code with the PHC refclock driver.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
510784077f conf: add minpoll option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9800e397fb hwclock: make minimum sampling separation configurable 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
1436d9961f conf: add precision option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
98f5d05925 ntp: include precision of PHC readings in their selection
Include a fixed non-zero precision (100 nanosecond) in the selection of
PHC readings.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
7a937c7652 conf: return hwtimestamp data in struct 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b198d76676 ntp: include precision in maxdelay test 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
97d4203354 ntp: adapt sampling separation for short polling intervals 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
beaaaad162 ntp: allow sub-second polling intervals
Change the minimum minpoll to -4, but keep the minimum maxpoll at 0 in
order to not make it too easy to flood distant servers.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4e78975909 ntp: use current poll when backing off on KoD RATE 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
99147ed8f2 ntp: rename maxdelay constants 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
dec0d3bfc2 ntp: reset ntpdata report on address change 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
cd84c99e70 examples: improve chronyd.service 2017-01-23 15:58:55 +01:00
165 changed files with 8402 additions and 3334 deletions

3
.gitignore vendored
View File

@@ -1,9 +1,12 @@
.deps
.vimrc
*.gcda
*.gcno
*.o
*.swp
*.dSYM
*.DS_Store
core.*
tags
/RELEASES
/Makefile

View File

@@ -36,8 +36,8 @@ DESTDIR=
HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
sys.o smooth.o tempcomp.o util.o $(HASH_OBJ)
reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \
smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@
@@ -64,13 +64,14 @@ chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
distclean : clean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
$(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
clean :
-rm -f *.o *.s chronyc chronyd core *~
-rm -f *.o *.s chronyc chronyd core.* *~
-rm -f *.gcda *.gcno
-rm -rf .deps
-rm -rf *.dSYM
@@ -109,9 +110,18 @@ install-docs :
%.s : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -S $<
check : chronyd chronyc
quickcheck : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run
cd test/system && ./run
check : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run -i 20 -m 2
cd test/system && ./run
print-chronyd-objects :
@echo $(OBJS) $(EXTRA_OBJS)
Makefile : Makefile.in configure
@echo

127
NEWS
View File

@@ -1,3 +1,130 @@
New in version 3.5.1
====================
Security fixes
--------------
* Create new file when writing pidfile (CVE-2020-14367)
New in version 3.5
==================
Enhancements
------------
* Add support for more accurate reading of PHC on Linux 5.0
* Add support for hardware timestamping on interfaces with read-only
timestamping configuration
* Add support for memory locking and real-time priority on FreeBSD,
NetBSD, Solaris
* Update seccomp filter to work on more architectures
* Validate refclock driver options
Bug fixes
---------
* Fix bindaddress directive on FreeBSD
* Fix transposition of hardware RX timestamp on Linux 4.13 and later
* Fix building on non-glibc systems
New in version 3.4
==================
Enhancements
------------
* Add filter option to server/pool/peer directive
* Add minsamples and maxsamples options to hwtimestamp directive
* Add support for faster frequency adjustments in Linux 4.19
* Change default pidfile to /var/run/chrony/chronyd.pid to allow
chronyd without root privileges to remove it on exit
* Disable sub-second polling intervals for distant NTP sources
* Extend range of supported sub-second polling intervals
* Get/set IPv4 destination/source address of NTP packets on FreeBSD
* Make burst options and command useful with short polling intervals
* Modify auto_offline option to activate when sending request failed
* Respond from interface that received NTP request if possible
* Add onoffline command to switch between online and offline state
according to current system network configuration
* Improve example NetworkManager dispatcher script
Bug fixes
---------
* Avoid waiting in Linux getrandom system call
* Fix PPS support on FreeBSD and NetBSD
New in version 3.3
==================
Enhancements
------------
* Add burst option to server/pool directive
* Add stratum and tai options to refclock directive
* Add support for Nettle crypto library
* Add workaround for missing kernel receive timestamps on Linux
* Wait for late hardware transmit timestamps
* Improve source selection with unreachable sources
* Improve protection against replay attacks on symmetric mode
* Allow PHC refclock to use socket in /var/run/chrony
* Add shutdown command to stop chronyd
* Simplify format of response to manual list command
* Improve handling of unknown responses in chronyc
Bug fixes
---------
* Respond to NTPv1 client requests with zero mode
* Fix -x option to not require CAP_SYS_TIME under non-root user
* Fix acquisitionport directive to work with privilege separation
* Fix handling of socket errors on Linux to avoid high CPU usage
* Fix chronyc to not get stuck in infinite loop after clock step
New in version 3.2
==================
Enhancements
------------
* Improve stability with NTP sources and reference clocks
* Improve stability with hardware timestamping
* Improve support for NTP interleaved modes
* Control frequency of system clock on macOS 10.13 and later
* Set TAI-UTC offset of system clock with leapsectz directive
* Minimise data in client requests to improve privacy
* Allow transmit-only hardware timestamping
* Add support for new timestamping options introduced in Linux 4.13
* Add root delay, root dispersion and maximum error to tracking log
* Add mindelay and asymmetry options to server/peer/pool directive
* Add extpps option to PHC refclock to timestamp external PPS signal
* Add pps option to refclock directive to treat any refclock as PPS
* Add width option to refclock directive to filter wrong pulse edges
* Add rxfilter option to hwtimestamp directive
* Add -x option to disable control of system clock
* Add -l option to log to specified file instead of syslog
* Allow multiple command-line options to be specified together
* Allow starting without root privileges with -Q option
* Update seccomp filter for new glibc versions
* Dump history on exit by default with dumpdir directive
* Use hardening compiler options by default
Bug fixes
---------
* Don't drop PHC samples with low-resolution system clock
* Ignore outliers in PHC tracking, RTC tracking, manual input
* Increase polling interval when peer is not responding
* Exit with error message when include directive fails
* Don't allow slash after hostname in allow/deny directive/command
* Try to connect to all addresses in chronyc before giving up
New in version 3.1
==================
Enhancements
------------
* Add support for precise cross timestamping of PHC on Linux
* Add minpoll, precision, nocrossts options to hwtimestamp directive
* Add rawmeasurements option to log directive and modify measurements
option to log only valid measurements from synchronised sources
* Allow sub-second polling interval with NTP sources
Bug fixes
---------
* Fix time smoothing in interleaved mode
New in version 3.0
==================

126
README
View File

@@ -4,7 +4,7 @@ What is chrony?
===============
chrony is a versatile implementation of the Network Time Protocol (NTP).
It can synchronize the system clock with NTP servers, reference clocks
It can synchronise the system clock with NTP servers, reference clocks
(e.g. GPS receiver), and manual input using wristwatch and keyboard.
It can also operate as an NTPv4 (RFC 5905) server and peer to provide
a time service to other computers in the network.
@@ -37,20 +37,16 @@ How do I set it up?
===================
The file INSTALL gives instructions. On supported systems the
compilation process should be automatic.
You will need an ANSI C compiler -- gcc is recommended.
The manual (in texinfo and text formats) describes how to set the
software up for the less straightforward cases.
compilation process should be automatic. You will need a C compiler,
e.g. gcc or clang.
What documentation is there?
============================
A manual is supplied in Texinfo format (chrony.texi) and
ready-formatted plain text (chrony.txt) in the distribution.
The distribution includes manual pages and a document containing
Frequently Asked Questions (FAQ).
There is also information available on the chrony web pages, accessible
The documentation is also available on the chrony web pages, accessible
through the URL
https://chrony.tuxfamily.org/
@@ -112,127 +108,57 @@ The following people have provided patches and other major contributions
to the program :
Lonnie Abelbeck <lonnie@abelbeck.com>
Patch to add tab-completion to chronyc
Benny Lyne Amorsen <benny@amorsen.dk>
Patch to add minstratum option
Andrew Bishop <amb@gedanken.demon.co.uk>
Fixes for bugs in logging when in daemon mode
Fixes for compiler warnings
Robustness improvements for drift file
Improve installation (directory checking etc)
Entries in contrib directory
Improvements to 'sources' and 'sourcestats' output from chronyc
Improvements to documentation
Investigation of required dosynctodr behaviour for various Solaris
versions.
Vincent Blut <vincent.debian@free.fr>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
Entries in contrib directory
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>
Entries in contrib directory
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Support for macOS
Support for privilege separation
Entries in contrib directory
Juliusz Chroboczek <jch@pps.jussieu.fr>
Fix install rule in Makefile if chronyd file is in use.
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
DNSchrony (in contrib directory), a tool for handling NTP servers
with variable IP addresses.
Stefan R. Filipek <srfilipek@gmail.com>
Mike Fleetwood <mike@rockover.demon.co.uk>
Fixes for compiler warnings
Alexander Gretencord <arutha@gmx.de>
Changes to installation directory system to make it easier for
package builders.
Andrew Griffiths <agriffit@redhat.com>
Patch to add support for seccomp filter
Walter Haidinger <walter.haidinger@gmx.at>
Providing me with login access to a Linux installation where v1.12
wouldn't compile, so I could develop the fixes for v1.13. Also, for
providing the disc space so I can keep an independent backup of the
sources.
Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
Port to NetBSD
John Hasler <john@dhh.gt.org>
Project and website at tuxfamily.org
Changes to support 64 bit machines (i.e. those where
sizeof(unsigned long) > 4)
Bug fix to initstepslew directive
Fix to remove potential buffer overrun errors.
Memory locking and real-time scheduler support
Fix fault where chronyd enters an endless loop
Tjalling Hattink <t.hattink@fugro.nl>
Fix scheduler to allow stepping clock from timeout handler
Patch to take leap second in PPS refclock from locked source
Patch to make reading of RTC for initial trim more reliable
Liam Hatton <me@liamhatton.com>
Advice on configuring for Linux on PPC
Jachym Holecek <jakym@volny.cz>
Patch to make Linux real time clock work with devfs
Håkan Johansson <f96hajo@chalmers.se>
Patch to avoid large values in sources and sourcestats output
Jim Knoble <jmknoble@pobox.com>
Fixes for compiler warnings
Antti Jrvinen <costello@iki.fi>
Advice on configuring for BSD/386
Eric Lammerts <eric@lammerts.org>
Stefan Lucke <stefan@lucke.in-berlin.de>
Victor Lum <viclum@vanu.com>
Kevin Lyda <kevin@ie.suberic.net>
Paul Menzel <paulepanter@users.sourceforge.net>
Vladimir Michl <vladimir.michl@seznam.cz>
Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
acquisitionport support
Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc
Andreas Piesk <apiesk@virbus.de>
Patch to make chronyc use the readline library if available
Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts
Bill Unruh <unruh@physics.ubc.ca>
Advice on statistics
Stephen Wadeley <swadeley@redhat.com>
Improvements to man pages
Bernhard Weiss <lisnablagh@web.de>
Wolfgang Weisselberg <weissel@netcologne.de>
Entries in contrib directory
Bernhard M. Wiedemann <bwiedemann@suse.de>
Joachim Wiedorn <ad_debian@joonet.de>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Many robustness and security improvements
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Providing me with information about the Linux 2.2 kernel
functionality compared to 2.0.
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86
Thomas Zajic <zlatko@zlatko.fdns.net>
Many other people have contributed bug reports and suggestions. We are sorry
we cannot identify all of you individually.

View File

@@ -50,8 +50,11 @@ typedef struct {
unsigned short port;
} NTP_Remote_Address;
#define INVALID_IF_INDEX -1
typedef struct {
IPAddr ip_addr;
int if_index;
int sock_fd;
} NTP_Local_Address;

View File

@@ -66,8 +66,6 @@ ARR_DestroyInstance(ARR_Instance array)
static void
realloc_array(ARR_Instance array, unsigned int min_size)
{
size_t data_size;
assert(min_size <= 2 * min_size);
if (array->allocated >= min_size && array->allocated <= 2 * min_size)
return;
@@ -79,10 +77,7 @@ realloc_array(ARR_Instance array, unsigned int min_size)
array->allocated = min_size;
}
data_size = (size_t)array->elem_size * array->allocated;
assert(data_size / array->elem_size == array->allocated);
array->data = Realloc(array->data, data_size);
array->data = Realloc2(array->data, array->allocated, array->elem_size);
}
void *

22
candm.h
View File

@@ -97,7 +97,11 @@
#define REQ_NTP_DATA 57
#define REQ_ADD_SERVER2 58
#define REQ_ADD_PEER2 59
#define N_REQUEST_TYPES 60
#define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61
#define REQ_SHUTDOWN 62
#define REQ_ONOFFLINE 63
#define N_REQUEST_TYPES 64
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -250,6 +254,7 @@ typedef struct {
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100
typedef struct {
IPAddr ip_addr;
@@ -267,8 +272,12 @@ typedef struct {
Float max_delay;
Float max_delay_ratio;
Float max_delay_dev_ratio;
Float min_delay;
Float asymmetry;
Float offset;
uint32_t flags;
int32_t filter_length;
uint32_t reserved[3];
int32_t EOR;
} REQ_NTP_Source;
@@ -362,8 +371,9 @@ typedef struct {
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types), new fields and flags in NTP source request
and report, new commands: ntpdata, refresh, serverstats
(using new request/reply types) and manual timestamp, added new fields and
flags to NTP source request and report, made length of manual list constant,
added new commands: ntpdata, refresh, serverstats, shutdown
*/
#define PROTO_VERSION_NUMBER 6
@@ -461,7 +471,9 @@ typedef struct {
#define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define RPY_NTP_DATA 16
#define N_REPLY_TYPES 17
#define RPY_MANUAL_TIMESTAMP2 17
#define RPY_MANUAL_LIST2 18
#define N_REPLY_TYPES 19
/* Status codes */
#define STT_SUCCESS 0
@@ -569,7 +581,7 @@ typedef struct {
} RPY_Rtc;
typedef struct {
uint32_t centiseconds;
Float offset;
Float dfreq_ppm;
Float new_afreq_ppm;
int32_t EOR;

428
client.c
View File

@@ -3,8 +3,8 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016
* Copyright (C) Miroslav Lichvar 2009-2016
* Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2018
*
* 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
@@ -81,8 +81,7 @@ int log_debug_enabled = 0;
void LOG_Message(LOG_Severity severity,
#if DEBUG > 0
LOG_Facility facility, int line_number,
const char *filename, const char *function_name,
int line_number, const char *filename, const char *function_name,
#endif
const char *format, ...)
{
@@ -112,7 +111,7 @@ read_line(void)
char *cmd;
rl_attempted_completion_function = command_name_completion;
rl_basic_word_break_characters = "\t\n\r";
rl_basic_word_break_characters = " \t\n\r";
/* save line only if not empty */
cmd = readline(prompt);
@@ -168,18 +167,18 @@ get_sockaddrs(const char *hostnames, int port)
addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >=
sizeof (addr->un.sun_path))
LOG_FATAL(LOGF_Client, "Unix socket path too long");
LOG_FATAL("Unix socket path too long");
addr->un.sun_family = AF_UNIX;
} else {
if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
DEBUG_LOG(LOGF_Client, "Could not get IP address for %s", hostname);
break;
DEBUG_LOG("Could not get IP address for %s", hostname);
continue;
}
for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr);
DEBUG_LOG(LOGF_Client, "Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
}
}
}
@@ -216,7 +215,7 @@ prepare_socket(union sockaddr_all *addr)
sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
DEBUG_LOG(LOGF_Client, "Could not create socket : %s", strerror(errno));
DEBUG_LOG("Could not create socket : %s", strerror(errno));
return 0;
}
@@ -229,7 +228,7 @@ prepare_socket(union sockaddr_all *addr)
dir = UTI_PathToDir(addr->un.sun_path);
if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path),
"%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path))
LOG_FATAL(LOGF_Client, "Unix socket path too long");
LOG_FATAL("Unix socket path too long");
Free(dir);
sa_un.sun_family = AF_UNIX;
@@ -237,19 +236,19 @@ prepare_socket(union sockaddr_all *addr)
/* Bind the socket to the path */
if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) {
DEBUG_LOG(LOGF_Client, "Could not bind socket : %s", strerror(errno));
DEBUG_LOG("Could not bind socket : %s", strerror(errno));
return 0;
}
/* Allow server without root privileges to send replies to our socket */
if (chmod(sa_un.sun_path, 0666) < 0) {
DEBUG_LOG(LOGF_Client, "Could not change socket permissions : %s", strerror(errno));
DEBUG_LOG("Could not change socket permissions : %s", strerror(errno));
return 0;
}
}
if (connect(sock_fd, &addr->sa, addr_len) < 0) {
DEBUG_LOG(LOGF_Client, "Could not connect socket : %s", strerror(errno));
DEBUG_LOG("Could not connect socket : %s", strerror(errno));
return 0;
}
@@ -269,7 +268,7 @@ close_io(void)
/* Remove our Unix domain socket */
if (getsockname(sock_fd, &addr.sa, &addr_len) < 0)
LOG_FATAL(LOGF_Client, "getsockname() failed : %s", strerror(errno));
LOG_FATAL("getsockname() failed : %s", strerror(errno));
if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) &&
addr.sa.sa_family == AF_UNIX)
unlink(addr.un.sun_path);
@@ -295,8 +294,7 @@ open_io(void)
/* Find an address for which a socket can be opened and connected */
for (; address_index < ARR_GetSize(sockaddrs); address_index++) {
addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index);
DEBUG_LOG(LOGF_Client, "Opening connection to %s",
UTI_SockaddrToString(&addr->sa));
DEBUG_LOG("Opening connection to %s", UTI_SockaddrToString(&addr->sa));
if (prepare_socket(addr))
return 1;
@@ -317,7 +315,7 @@ bits_to_mask(int bits, int family, IPAddr *mask)
mask->family = family;
switch (family) {
case IPADDR_INET4:
if (bits < 0)
if (bits > 32 || bits < 0)
bits = 32;
if (bits > 0) {
mask->addr.in4 = -1;
@@ -373,13 +371,13 @@ read_mask_address(char *line, IPAddr *mask, IPAddr *address)
bits_to_mask(-1, address->family, mask);
return 1;
} else {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
LOG(LOGS_ERR, "Could not get address for hostname");
return 0;
}
}
}
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for mask/address");
LOG(LOGS_ERR, "Invalid syntax for mask/address");
return 0;
}
@@ -428,6 +426,14 @@ process_cmd_online(CMD_Request *msg, char *line)
/* ================================================== */
static void
process_cmd_onoffline(CMD_Request *msg, char *line)
{
msg->command = htons(REQ_ONOFFLINE);
}
/* ================================================== */
static int
read_address_integer(char *line, IPAddr *address, int *value)
{
@@ -438,11 +444,11 @@ read_address_integer(char *line, IPAddr *address, int *value)
line = CPS_SplitWord(line);
if (sscanf(line, "%d", value) != 1) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address value");
LOG(LOGS_ERR, "Invalid syntax for address value");
ok = 0;
} else {
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0;
} else {
ok = 1;
@@ -466,11 +472,11 @@ read_address_double(char *line, IPAddr *address, double *value)
line = CPS_SplitWord(line);
if (sscanf(line, "%lf", value) != 1) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address value");
LOG(LOGS_ERR, "Invalid syntax for address value");
ok = 0;
} else {
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0;
} else {
ok = 1;
@@ -703,7 +709,7 @@ process_cmd_burst(CMD_Request *msg, char *line)
CPS_SplitWord(s2);
if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for burst command");
LOG(LOGS_ERR, "Invalid syntax for burst command");
return 0;
}
@@ -735,7 +741,7 @@ process_cmd_local(CMD_Request *msg, char *line)
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
on_off = 1;
} else {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for local command");
LOG(LOGS_ERR, "Invalid syntax for local command");
return 0;
}
@@ -764,7 +770,7 @@ process_cmd_manual(CMD_Request *msg, const char *line)
} else if (!strcmp(p, "reset")) {
msg->data.manual.option = htonl(2);
} else {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for manual command");
LOG(LOGS_ERR, "Invalid syntax for manual command");
return 0;
}
msg->command = htons(REQ_MANUAL);
@@ -798,8 +804,8 @@ parse_allow_deny(CMD_Request *msg, char *line)
(n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) {
/* Try to parse as the name of a machine */
if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not read address");
if (slashpos || DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
LOG(LOGS_ERR, "Could not read address");
return 0;
} else {
UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
@@ -852,7 +858,7 @@ parse_allow_deny(CMD_Request *msg, char *line)
if (n == 1) {
msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits);
} else {
LOG(LOGS_WARN, LOGF_Client, "Warning: badly formatted subnet size, using %d",
LOG(LOGS_WARN, "Warning: badly formatted subnet size, using %d",
(int)ntohl(msg->data.allow_deny.subnet_bits));
}
}
@@ -987,7 +993,7 @@ process_cmd_accheck(CMD_Request *msg, char *line)
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
return 1;
} else {
LOG(LOGS_ERR, LOGF_Client, "Could not read address");
LOG(LOGS_ERR, "Could not read address");
return 0;
}
}
@@ -1003,7 +1009,7 @@ process_cmd_cmdaccheck(CMD_Request *msg, char *line)
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
return 1;
} else {
LOG(LOGS_ERR, LOGF_Client, "Could not read address");
LOG(LOGS_ERR, "Could not read address");
return 0;
}
}
@@ -1073,17 +1079,17 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
status = CPS_ParseNTPSourceAdd(line, &data);
switch (status) {
case 0:
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for add command");
LOG(LOGS_ERR, "Invalid syntax for add command");
break;
default:
if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Invalid host/IP address");
LOG(LOGS_ERR, "Invalid host/IP address");
break;
}
opt_name = NULL;
if (opt_name) {
LOG(LOGS_ERR, LOGF_Client, "%s can't be set in chronyc", opt_name);
LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name);
break;
}
@@ -1103,16 +1109,22 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio);
msg->data.ntp_source.max_delay_dev_ratio =
UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio);
msg->data.ntp_source.min_delay = UTI_FloatHostToNetwork(data.params.min_delay);
msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry);
msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
msg->data.ntp_source.flags = htonl(
(data.params.online ? REQ_ADDSRC_ONLINE : 0) |
(data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) |
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
(data.params.burst ? REQ_ADDSRC_BURST : 0) |
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
result = 1;
break;
@@ -1126,7 +1138,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
static int
process_cmd_add_server(CMD_Request *msg, char *line)
{
msg->command = htons(REQ_ADD_SERVER2);
msg->command = htons(REQ_ADD_SERVER3);
return process_cmd_add_server_or_peer(msg, line);
}
@@ -1135,7 +1147,7 @@ process_cmd_add_server(CMD_Request *msg, char *line)
static int
process_cmd_add_peer(CMD_Request *msg, char *line)
{
msg->command = htons(REQ_ADD_PEER2);
msg->command = htons(REQ_ADD_PEER3);
return process_cmd_add_server_or_peer(msg, line);
}
@@ -1153,11 +1165,11 @@ process_cmd_delete(CMD_Request *msg, char *line)
CPS_SplitWord(line);
if (!*hostname) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address");
LOG(LOGS_ERR, "Invalid syntax for address");
ok = 0;
} else {
if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0;
} else {
UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr);
@@ -1205,6 +1217,8 @@ give_help(void)
"minstratum <address> <stratum>\0Modify minimum stratum\0"
"offline [<mask>/<address>]\0Set sources in subnet to offline status\0"
"online [<mask>/<address>]\0Set sources in subnet to online status\0"
"onoffline\0Set all sources to online or offline status\0"
"\0according to network configuration\0"
"polltarget <address> <target>\0Modify poll target\0"
"refresh\0Refresh IP addresses\0"
"\0\0"
@@ -1242,6 +1256,8 @@ give_help(void)
"Other daemon commands:\0\0"
"cyclelogs\0Close and re-open log files\0"
"dump\0Dump all measurements to save files\0"
"rekey\0Re-read keys from key file\0"
"shutdown\0Stop daemon\0"
"\0\0"
"Client commands:\0\0"
"dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0"
@@ -1265,30 +1281,52 @@ give_help(void)
/* Tab-completion when editline/readline is available */
#ifdef FEAT_READLINE
enum {
TAB_COMPLETE_BASE_CMDS,
TAB_COMPLETE_ADD_OPTS,
TAB_COMPLETE_MANUAL_OPTS,
TAB_COMPLETE_SOURCES_OPTS,
TAB_COMPLETE_SOURCESTATS_OPTS,
TAB_COMPLETE_MAX_INDEX
};
static int tab_complete_index;
static char *
command_name_generator(const char *text, int state)
{
const char *name, *names[] = {
"accheck", "activity", "add peer", "add server", "allow", "burst",
const char *name, **names[TAB_COMPLETE_MAX_INDEX];
const char *base_commands[] = {
"accheck", "activity", "add", "allow", "burst",
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
"manual on", "manual off", "manual delete", "manual list", "manual reset",
"maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
"retries", "rtcdata", "serverstats", "settime", "smoothing", "smoothtime",
"sources", "sources -v", "sourcestats", "sourcestats -v", "timeout",
"tracking", "trimrtc", "waitsync", "writertc",
"retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
"smoothtime", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
const char *add_options[] = { "peer", "server", NULL };
const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
const char *sources_options[] = { "-v", NULL };
const char *sourcestats_options[] = { "-v", NULL };
static int list_index, len;
names[TAB_COMPLETE_BASE_CMDS] = base_commands;
names[TAB_COMPLETE_ADD_OPTS] = add_options;
names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
names[TAB_COMPLETE_SOURCES_OPTS] = sources_options;
names[TAB_COMPLETE_SOURCESTATS_OPTS] = sourcestats_options;
if (!state) {
list_index = 0;
len = strlen(text);
}
while ((name = names[list_index++])) {
while ((name = names[tab_complete_index][list_index++])) {
if (strncmp(name, text, len) == 0) {
return strdup(name);
}
@@ -1302,7 +1340,25 @@ command_name_generator(const char *text, int state)
static char **
command_name_completion(const char *text, int start, int end)
{
char first[32];
snprintf(first, MIN(sizeof (first), start + 1), "%s", rl_line_buffer);
rl_attempted_completion_over = 1;
if (!strcmp(first, "add ")) {
tab_complete_index = TAB_COMPLETE_ADD_OPTS;
} else if (!strcmp(first, "manual ")) {
tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
} else if (!strcmp(first, "sources ")) {
tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
} else if (!strcmp(first, "sourcestats ")) {
tab_complete_index = TAB_COMPLETE_SOURCESTATS_OPTS;
} else if (first[0] == '\0') {
tab_complete_index = TAB_COMPLETE_BASE_CMDS;
} else {
return NULL;
}
return rl_completion_matches(text, command_name_generator);
}
#endif
@@ -1321,18 +1377,16 @@ static int proto_version = PROTO_VERSION_NUMBER;
static int
submit_request(CMD_Request *request, CMD_Reply *reply)
{
int bad_length, bad_sequence, bad_header;
int select_status;
int recv_status;
int read_length;
int expected_length;
int command_length;
int padding_length;
struct timespec ts_now, ts_start;
struct timeval tv;
int n_attempts, new_attempt;
double timeout;
fd_set rdfd, wrfd, exfd;
fd_set rdfd;
request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0;
@@ -1344,15 +1398,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
new_attempt = 1;
do {
if (gettimeofday(&tv, NULL))
return 0;
if (new_attempt) {
new_attempt = 0;
if (n_attempts > max_retries)
return 0;
if (gettimeofday(&tv, NULL))
return 0;
UTI_TimevalToTimespec(&tv, &ts_start);
UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
@@ -1368,22 +1422,18 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
memset(((char *)request) + command_length - padding_length, 0, padding_length);
if (sock_fd < 0) {
DEBUG_LOG(LOGF_Client, "No socket to send request");
DEBUG_LOG("No socket to send request");
return 0;
}
if (send(sock_fd, (void *)request, command_length, 0) < 0) {
DEBUG_LOG(LOGF_Client, "Could not send %d bytes : %s",
command_length, strerror(errno));
DEBUG_LOG("Could not send %d bytes : %s", command_length, strerror(errno));
return 0;
}
DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length);
DEBUG_LOG("Sent %d bytes", command_length);
}
if (gettimeofday(&tv, NULL))
return 0;
UTI_TimevalToTimespec(&tv, &ts_now);
/* Check if the clock wasn't stepped back */
@@ -1392,22 +1442,27 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
DEBUG_LOG("Timeout %f seconds", timeout);
/* Avoid calling select() with an invalid timeout */
if (timeout <= 0.0) {
new_attempt = 1;
continue;
}
UTI_DoubleToTimeval(timeout, &tv);
DEBUG_LOG(LOGF_Client, "Timeout %f seconds", timeout);
FD_ZERO(&rdfd);
FD_ZERO(&wrfd);
FD_ZERO(&exfd);
FD_SET(sock_fd, &rdfd);
if (quit)
return 0;
select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &tv);
select_status = select(sock_fd + 1, &rdfd, NULL, NULL, &tv);
if (select_status < 0) {
DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno));
DEBUG_LOG("select failed : %s", strerror(errno));
return 0;
} else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */
new_attempt = 1;
@@ -1417,40 +1472,24 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
if (recv_status < 0) {
/* If we get connrefused here, it suggests the sendto is
going to a dead port */
DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno));
DEBUG_LOG("Could not receive : %s", strerror(errno));
new_attempt = 1;
} else {
DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status);
DEBUG_LOG("Received %d bytes", recv_status);
read_length = recv_status;
if (read_length >= offsetof(CMD_Reply, data)) {
expected_length = PKL_ReplyLength(reply);
} else {
expected_length = 0;
}
bad_length = (read_length < expected_length ||
expected_length < offsetof(CMD_Reply, data));
if (!bad_length) {
bad_sequence = reply->sequence != request->sequence;
} else {
bad_sequence = 0;
}
if (bad_length || bad_sequence) {
continue;
}
bad_header = ((reply->version != proto_version &&
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
ntohs(reply->status) == STT_BADPKTVERSION)) ||
(reply->pkt_type != PKT_TYPE_CMD_REPLY) ||
(reply->res1 != 0) ||
(reply->res2 != 0) ||
(reply->command != request->command));
if (bad_header) {
/* Check if the header is valid */
if (read_length < offsetof(CMD_Reply, data) ||
(reply->version != proto_version &&
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
ntohs(reply->status) == STT_BADPKTVERSION)) ||
reply->pkt_type != PKT_TYPE_CMD_REPLY ||
reply->res1 != 0 ||
reply->res2 != 0 ||
reply->command != request->command ||
reply->sequence != request->sequence) {
DEBUG_LOG("Invalid reply");
continue;
}
@@ -1469,8 +1508,17 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#error unknown compatibility with PROTO_VERSION - 1
#endif
/* Check that the packet contains all data it is supposed to have.
Unknown responses will always pass this test as their expected
length is zero. */
if (read_length < PKL_ReplyLength(reply)) {
DEBUG_LOG("Reply too short");
new_attempt = 1;
continue;
}
/* Good packet received, print out results */
DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d",
DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
break;
}
@@ -1575,6 +1623,9 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
return 0;
}
/* Make sure an unknown response was not requested */
assert(PKL_ReplyLength(reply));
return 1;
}
@@ -1588,17 +1639,17 @@ print_seconds(unsigned long s)
if (s == (uint32_t)-1) {
printf(" -");
} else if (s < 1200) {
printf("%4ld", s);
printf("%4lu", s);
} else if (s < 36000) {
printf("%3ldm", s / 60);
printf("%3lum", s / 60);
} else if (s < 345600) {
printf("%3ldh", s / 3600);
printf("%3luh", s / 3600);
} else {
d = s / 86400;
if (d > 999) {
printf("%3ldy", d / 365);
printf("%3luy", d / 365);
} else {
printf("%3ldd", d);
printf("%3lud", d);
}
}
}
@@ -2060,7 +2111,8 @@ process_cmd_sources(char *line)
mode = ntohs(reply.data.source_data.mode);
UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
format_name(name, sizeof (name), 25, mode == RPY_SD_MD_REF,
format_name(name, sizeof (name), 25,
mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
ip_addr.addr.in4, &ip_addr);
switch (mode) {
@@ -2215,8 +2267,8 @@ process_cmd_tracking(char *line)
"Frequency : %.3F\n"
"Residual freq : %+.3f ppm\n"
"Skew : %.3f ppm\n"
"Root delay : %.6f seconds\n"
"Root dispersion : %.6f seconds\n"
"Root delay : %.9f seconds\n"
"Root dispersion : %.9f seconds\n"
"Update interval : %.1f seconds\n"
"Leap status : %L\n",
(unsigned long)ref_id, name,
@@ -2263,7 +2315,7 @@ process_cmd_ntpdata(char *line)
for (i = 0; i < n_sources; i++) {
if (specified_addr) {
if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
LOG(LOGS_ERR, "Could not get address for hostname");
return 0;
}
} else {
@@ -2420,7 +2472,7 @@ process_cmd_smoothtime(CMD_Request *msg, const char *line)
} else if (!strcmp(line, "activate")) {
msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE);
} else {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for smoothtime command");
LOG(LOGS_ERR, "Invalid syntax for smoothtime command");
return 0;
}
@@ -2537,7 +2589,7 @@ process_cmd_manual_list(const char *line)
struct timespec when;
request.command = htons(REQ_MANUAL_LIST);
if (!request_reply(&request, &reply, RPY_MANUAL_LIST, 0))
if (!request_reply(&request, &reply, RPY_MANUAL_LIST2, 0))
return 0;
n_samples = ntohl(reply.data.manual_list.n_samples);
@@ -2545,7 +2597,7 @@ process_cmd_manual_list(const char *line)
print_header("# Date Time(UTC) Slewed Original Residual");
for (i = 0; i < n_samples; i++) {
for (i = 0; i < n_samples && i < MAX_MANUAL_LIST_SAMPLES; i++) {
sample = &reply.data.manual_list.samples[i];
UTI_TimespecNetworkToHost(&sample->when, &when);
@@ -2568,7 +2620,7 @@ process_cmd_manual_delete(CMD_Request *msg, const char *line)
int index;
if (sscanf(line, "%d", &index) != 1) {
LOG(LOGS_ERR, LOGF_Client, "Bad syntax for manual delete command");
LOG(LOGS_ERR, "Bad syntax for manual delete command");
return 0;
}
@@ -2586,7 +2638,6 @@ process_cmd_settime(char *line)
time_t now, new_time;
CMD_Request request;
CMD_Reply reply;
long offset_cs;
double dfreq_ppm, new_afreq_ppm;
double offset;
@@ -2600,9 +2651,8 @@ process_cmd_settime(char *line)
ts.tv_nsec = 0;
UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts);
request.command = htons(REQ_SETTIME);
if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP, 1)) {
offset_cs = ntohl(reply.data.manual_timestamp.centiseconds);
offset = 0.01 * (double)(int32_t)offset_cs;
if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP2, 1)) {
offset = UTI_FloatNetworkToHost(reply.data.manual_timestamp.offset);
dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm);
new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm);
printf("Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm\n",
@@ -2631,7 +2681,7 @@ process_cmd_makestep(CMD_Request *msg, char *line)
if (*line) {
if (sscanf(line, "%lf %d", &threshold, &limit) != 2) {
LOG(LOGS_ERR, LOGF_Client, "Bad syntax for makestep command");
LOG(LOGS_ERR, "Bad syntax for makestep command");
return 0;
}
msg->command = htons(REQ_MODIFY_MAKESTEP);
@@ -2708,6 +2758,14 @@ process_cmd_refresh(CMD_Request *msg, char *line)
/* ================================================== */
static void
process_cmd_shutdown(CMD_Request *msg, char *line)
{
msg->command = htons(REQ_SHUTDOWN);
}
/* ================================================== */
static int
process_cmd_waitsync(char *line)
{
@@ -2780,7 +2838,7 @@ process_cmd_dns(const char *line)
} else if (!strcmp(line, "+n")) {
no_dns = 0;
} else {
LOG(LOGS_ERR, LOGF_Client, "Unrecognized dns command");
LOG(LOGS_ERR, "Unrecognized dns command");
return 0;
}
return 1;
@@ -2795,7 +2853,7 @@ process_cmd_timeout(const char *line)
timeout = atoi(line);
if (timeout < 100) {
LOG(LOGS_ERR, LOGF_Client, "Timeout %d is too short", timeout);
LOG(LOGS_ERR, "Timeout %d is too short", timeout);
return 0;
}
initial_timeout = timeout;
@@ -2810,8 +2868,8 @@ process_cmd_retries(const char *line)
int retries;
retries = atoi(line);
if (retries < 0) {
LOG(LOGS_ERR, LOGF_Client, "Invalid maximum number of retries");
if (retries < 0 || retries > 30) {
LOG(LOGS_ERR, "Invalid maximum number of retries");
return 0;
}
max_retries = retries;
@@ -2833,12 +2891,12 @@ process_cmd_keygen(char *line)
snprintf(hash_name, sizeof (hash_name), "MD5");
#endif
if (sscanf(line, "%u %16s %d", &id, hash_name, &bits))
if (sscanf(line, "%u %16s %u", &id, hash_name, &bits))
;
length = CLAMP(10, (bits + 7) / 8, sizeof (key));
if (HSH_GetHashId(hash_name) < 0) {
LOG(LOGS_ERR, LOGF_Client, "Unknown hash function %s", hash_name);
LOG(LOGS_ERR, "Unknown hash function %s", hash_name);
return 0;
}
@@ -2977,6 +3035,8 @@ process_line(char *line)
do_normal_submit = process_cmd_offline(&tx_message, line);
} else if (!strcmp(command, "online")) {
do_normal_submit = process_cmd_online(&tx_message, line);
} else if (!strcmp(command, "onoffline")) {
process_cmd_onoffline(&tx_message, line);
} else if (!strcmp(command, "polltarget")) {
do_normal_submit = process_cmd_polltarget(&tx_message, line);
} else if (!strcmp(command, "quit")) {
@@ -3003,6 +3063,8 @@ process_line(char *line)
} else if (!strcmp(command, "settime")) {
do_normal_submit = 0;
ret = process_cmd_settime(line);
} else if (!strcmp(command, "shutdown")) {
process_cmd_shutdown(&tx_message, line);
} else if (!strcmp(command, "smoothing")) {
do_normal_submit = 0;
ret = process_cmd_smoothing(line);
@@ -3030,11 +3092,11 @@ process_line(char *line)
} else if (!strcmp(command, "authhash") ||
!strcmp(command, "password")) {
/* Warn, but don't return error to not break scripts */
LOG(LOGS_WARN, LOGF_Client, "Authentication is no longer supported");
LOG(LOGS_WARN, "Authentication is no longer supported");
do_normal_submit = 0;
ret = 1;
} else {
LOG(LOGS_ERR, LOGF_Client, "Unrecognized command");
LOG(LOGS_ERR, "Unrecognized command");
do_normal_submit = 0;
}
@@ -3051,7 +3113,7 @@ process_line(char *line)
static int
process_args(int argc, char **argv, int multi)
{
int total_length, i, ret;
int total_length, i, ret = 0;
char *line;
total_length = 0;
@@ -3097,7 +3159,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2017 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2019 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",
@@ -3106,54 +3168,80 @@ display_gpl(void)
/* ================================================== */
static void
print_help(const char *progname)
{
printf("Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]\n",
progname);
}
/* ================================================== */
static void
print_version(void)
{
printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
}
/* ================================================== */
int
main(int argc, char **argv)
{
char *line;
const char *progname = argv[0];
const char *hostnames = NULL;
int ret = 1, multi = 0, family = IPADDR_UNSPEC;
int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
int port = DEFAULT_CANDM_PORT;
/* Parse command line options */
while (++argv, --argc) {
if (!strcmp(*argv, "-h")) {
++argv, --argc;
if (*argv) {
hostnames = *argv;
}
} else if (!strcmp(*argv, "-p")) {
++argv, --argc;
if (*argv) {
port = atoi(*argv);
}
} else if (!strcmp(*argv, "-f")) {
++argv, --argc;
/* For compatibility */
} else if (!strcmp(*argv, "-a")) {
/* For compatibility */
} else if (!strcmp(*argv, "-c")) {
csv_mode = 1;
} else if (!strcmp(*argv, "-d")) {
log_debug_enabled = 1;
} else if (!strcmp(*argv, "-m")) {
multi = 1;
} else if (!strcmp(*argv, "-n")) {
no_dns = 1;
} else if (!strcmp(*argv, "-4")) {
family = IPADDR_INET4;
} else if (!strcmp(*argv, "-6")) {
family = IPADDR_INET6;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
/* Parse (undocumented) long command-line options */
for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) {
print_help(progname);
return 0;
} else if (!strncmp(*argv, "-", 1)) {
LOG(LOGS_ERR, LOGF_Client,
"Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]",
progname);
return 1;
} else {
break; /* And process remainder of line as a command */
} else if (!strcmp("--version", argv[optind])) {
print_version();
return 0;
}
}
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "+46acdf:h:mnp:v")) != -1) {
switch (opt) {
case '4':
case '6':
family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
break;
case 'a':
case 'f':
/* For compatibility only */
break;
case 'c':
csv_mode = 1;
break;
case 'd':
log_debug_enabled = 1;
break;
case 'h':
hostnames = optarg;
break;
case 'm':
multi = 1;
break;
case 'n':
no_dns = 1;
break;
case 'p':
port = atoi(optarg);
break;
case 'v':
print_version();
return 0;
default:
print_help(progname);
return 1;
}
}
@@ -3161,7 +3249,7 @@ main(int argc, char **argv)
on_terminal = 1;
}
if (on_terminal && (argc == 0)) {
if (on_terminal && optind == argc) {
display_gpl();
}
@@ -3171,15 +3259,15 @@ main(int argc, char **argv)
hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1";
}
UTI_SetQuitSignalsHandler(signal_handler);
UTI_SetQuitSignalsHandler(signal_handler, 0);
sockaddrs = get_sockaddrs(hostnames, port);
if (!open_io())
LOG_FATAL(LOGF_Client, "Could not open connection to daemon");
LOG_FATAL("Could not open connection to daemon");
if (argc > 0) {
ret = process_args(argc, argv, multi);
if (optind < argc) {
ret = process_args(argc - optind, argv + optind, multi);
} else {
do {
line = read_line();

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2016
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
*
* 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
@@ -128,6 +128,9 @@ static int cmd_leak_rate;
/* Flag indicating whether the last response was dropped */
#define FLAG_NTP_DROPPED 0x1
/* NTP limit interval in log2 */
static int ntp_limit_interval;
/* Flag indicating whether facility is turned on or not */
static int active;
@@ -294,7 +297,7 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
*max_tokens = *tokens_per_packet * burst;
DEBUG_LOG(LOGF_ClientLog, "Tokens max %d packet %d shift %d",
DEBUG_LOG("Tokens max %d packet %d shift %d",
*max_tokens, *tokens_per_packet, *token_shift);
}
@@ -309,11 +312,13 @@ CLG_Initialise(void)
ntp_tokens_per_packet = cmd_tokens_per_packet = 0;
ntp_token_shift = cmd_token_shift = 0;
ntp_leak_rate = cmd_leak_rate = 0;
ntp_limit_interval = MIN_LIMIT_INTERVAL;
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) {
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
&ntp_token_shift);
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
}
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) {
@@ -325,7 +330,7 @@ CLG_Initialise(void)
active = !CNF_GetNoClientLog();
if (!active) {
if (ntp_leak_rate || cmd_leak_rate)
LOG_FATAL(LOGF_ClientLog, "ratelimit cannot be used with noclientlog");
LOG_FATAL("ratelimit cannot be used with noclientlog");
return;
}
@@ -470,7 +475,7 @@ CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
record->flags & FLAG_NTP_DROPPED ?
&record->ntp_timeout_rate : &record->ntp_rate);
DEBUG_LOG(LOGF_ClientLog, "NTP hits %"PRIu32" rate %d trate %d tokens %d",
DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d",
record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
record->ntp_tokens);
@@ -494,7 +499,7 @@ CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
&record->cmd_rate);
DEBUG_LOG(LOGF_ClientLog, "Cmd hits %"PRIu32" rate %d tokens %d",
DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d",
record->cmd_hits, record->cmd_rate, record->cmd_tokens);
return get_index(record);
@@ -606,6 +611,14 @@ void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
/* ================================================== */
int
CLG_GetNtpMinPoll(void)
{
return ntp_limit_interval;
}
/* ================================================== */
int
CLG_GetNumberOfIndices(void)
{

View File

@@ -39,6 +39,7 @@ extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
extern int CLG_GetNtpMinPoll(void);
/* And some reporting functions, for use by chronyc. */

115
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016
* Copyright (C) Miroslav Lichvar 2009-2016, 2018
*
* 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
@@ -136,6 +136,10 @@ static const char permissions[] = {
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 */
};
/* ================================================== */
@@ -161,7 +165,7 @@ prepare_socket(int family, int port_number)
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not open %s command socket : %s",
LOG(LOGS_ERR, "Could not open %s command socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
return -1;
}
@@ -172,14 +176,14 @@ prepare_socket(int family, int port_number)
if (family != AF_UNIX) {
/* Allow reuse of port number */
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
LOG(LOGS_ERR, "Could not set reuseaddr socket options");
/* Don't quit - we might survive anyway */
}
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option");
LOG(LOGS_ERR, "Could not set free bind socket option");
}
#endif
@@ -188,7 +192,7 @@ prepare_socket(int family, int port_number)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not request IPV6_V6ONLY socket option");
LOG(LOGS_ERR, "Could not request IPV6_V6ONLY socket option");
}
#endif
}
@@ -230,7 +234,7 @@ prepare_socket(int family, int port_number)
my_addr.un.sun_family = family;
if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s",
CNF_GetBindCommandPath()) >= sizeof (my_addr.un.sun_path))
LOG_FATAL(LOGF_CmdMon, "Unix socket path too long");
LOG_FATAL("Unix socket path too long");
unlink(my_addr.un.sun_path);
break;
default:
@@ -238,7 +242,7 @@ prepare_socket(int family, int port_number)
}
if (bind(sock_fd, &my_addr.sa, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not bind %s command socket : %s",
LOG(LOGS_ERR, "Could not bind %s command socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd);
return -1;
@@ -276,7 +280,6 @@ do_size_checks(void)
for (i = 1; i < N_REPLY_TYPES; i++) {
reply.reply = htons(i);
reply.status = STT_SUCCESS;
reply.data.manual_list.n_samples = htonl(MAX_MANUAL_LIST_SAMPLES);
reply_length = PKL_ReplyLength(&reply);
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
reply_length > sizeof (CMD_Reply))
@@ -315,7 +318,7 @@ CAM_Initialise(int family)
&& sock_fd6 < 0
#endif
) {
LOG_FATAL(LOGF_CmdMon, "Could not open any command socket");
LOG_FATAL("Could not open any command socket");
}
access_auth_table = ADF_CreateTable();
@@ -396,12 +399,12 @@ transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to)
&where_to->sa, addrlen);
if (status < 0) {
DEBUG_LOG(LOGF_CmdMon, "Could not send to %s fd %d : %s",
DEBUG_LOG("Could not send to %s fd %d : %s",
UTI_SockaddrToString(&where_to->sa), sock_fd, strerror(errno));
return;
}
DEBUG_LOG(LOGF_CmdMon, "Sent %d bytes to %s fd %d", status,
DEBUG_LOG("Sent %d bytes to %s fd %d", status,
UTI_SockaddrToString(&where_to->sa), sock_fd);
}
@@ -422,7 +425,7 @@ handle_online(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.online.address, &address);
if (!NSR_TakeSourcesOnline(&mask, &address))
if (!NSR_SetConnectivity(&mask, &address, SRC_ONLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
@@ -435,12 +438,24 @@ handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.offline.address, &address);
if (!NSR_TakeSourcesOffline(&mask, &address))
if (!NSR_SetConnectivity(&mask, &address, SRC_OFFLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_onoffline(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address, mask;
address.family = mask.family = IPADDR_UNSPEC;
if (!NSR_SetConnectivity(&mask, &address, SRC_MAYBE_ONLINE))
;
}
/* ================================================== */
static void
handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message)
{
@@ -568,14 +583,13 @@ static void
handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
{
struct timespec ts;
long offset_cs;
double dfreq_ppm, new_afreq_ppm;
double offset, dfreq_ppm, new_afreq_ppm;
UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts);
if (!MNL_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED);
} else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP);
tx_message->data.manual_timestamp.centiseconds = htonl((int32_t)offset_cs);
} else if (MNL_AcceptTimestamp(&ts, &offset, &dfreq_ppm, &new_afreq_ppm)) {
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP2);
tx_message->data.manual_timestamp.offset = UTI_FloatHostToNetwork(offset);
tx_message->data.manual_timestamp.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm);
tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm);
} else {
@@ -786,18 +800,23 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
params.max_delay_dev_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
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.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
@@ -874,7 +893,7 @@ handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
double dfreq;
dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq);
LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6);
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta freq of %.3fppm", dfreq);
LOG(LOGS_INFO, "Accumulated delta freq of %.3fppm", dfreq);
}
/* ================================================== */
@@ -887,7 +906,7 @@ handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
sec = (int32_t)ntohl(rx_message->data.doffset.sec);
usec = (int32_t)ntohl(rx_message->data.doffset.usec);
doffset = (double) sec + 1.0e-6 * (double) usec;
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset);
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
LCL_AccumulateOffset(doffset, 0.0);
}
@@ -1065,9 +1084,6 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
memset(tx_message->data.client_accesses_by_index.clients, 0,
sizeof (tx_message->data.client_accesses_by_index.clients));
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
continue;
@@ -1100,10 +1116,11 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
RPY_ManualListSample *sample;
RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES];
tx_message->reply = htons(RPY_MANUAL_LIST);
tx_message->reply = htons(RPY_MANUAL_LIST2);
MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples);
tx_message->data.manual_list.n_samples = htonl(n_samples);
for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i];
UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
@@ -1235,6 +1252,15 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
/* ================================================== */
static void
handle_shutdown(CMD_Request *rx_message, CMD_Reply *tx_message)
{
LOG(LOGS_INFO, "Received shutdown command");
SCH_QuitProgram();
}
/* ================================================== */
/* Read a packet and process it */
@@ -1258,14 +1284,14 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
&where_from.sa, &from_length);
if (status < 0) {
LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket %d",
LOG(LOGS_WARN, "Error [%s] reading from control socket %d",
strerror(errno), sock_fd);
return;
}
if (from_length > sizeof (where_from) ||
from_length <= sizeof (where_from.sa.sa_family)) {
DEBUG_LOG(LOGF_CmdMon, "Read command packet without source address");
DEBUG_LOG("Read command packet without source address");
return;
}
@@ -1300,7 +1326,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
assert(0);
}
DEBUG_LOG(LOGF_CmdMon, "Received %d bytes from %s fd %d",
DEBUG_LOG("Received %d bytes from %s fd %d",
status, UTI_SockaddrToString(&where_from.sa), sock_fd);
if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) {
@@ -1319,29 +1345,24 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* We don't know how to process anything like this or an error reply
would be larger than the request */
DEBUG_LOG(LOGF_CmdMon, "Command packet dropped");
DEBUG_LOG("Command packet dropped");
return;
}
expected_length = PKL_CommandLength(&rx_message);
rx_command = ntohs(rx_message.command);
memset(&tx_message, 0, sizeof (tx_message));
tx_message.version = PROTO_VERSION_NUMBER;
tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
tx_message.res1 = 0;
tx_message.res2 = 0;
tx_message.command = rx_message.command;
tx_message.reply = htons(RPY_NULL);
tx_message.status = htons(STT_SUCCESS);
tx_message.pad1 = 0;
tx_message.pad2 = 0;
tx_message.pad3 = 0;
tx_message.sequence = rx_message.sequence;
tx_message.pad4 = 0;
tx_message.pad5 = 0;
if (rx_message.version != PROTO_VERSION_NUMBER) {
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid version (%d != %d)",
DEBUG_LOG("Command packet has invalid version (%d != %d)",
rx_message.version, PROTO_VERSION_NUMBER);
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
@@ -1353,7 +1374,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
if (rx_command >= N_REQUEST_TYPES ||
expected_length < (int)offsetof(CMD_Request, data)) {
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid command %d", rx_command);
DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID);
transmit_reply(&tx_message, &where_from);
@@ -1361,7 +1382,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
}
if (read_length < expected_length) {
DEBUG_LOG(LOGF_CmdMon, "Command packet is too short (%d < %d)", read_length,
DEBUG_LOG("Command packet is too short (%d < %d)", read_length,
expected_length);
tx_message.status = htons(STT_BADPKTLENGTH);
@@ -1376,7 +1397,7 @@ 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_LimitCommandResponseRate(log_index)) {
DEBUG_LOG(LOGF_CmdMon, "Command packet discarded to limit response rate");
DEBUG_LOG("Command packet discarded to limit response rate");
return;
}
@@ -1526,11 +1547,11 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_cmdaccheck(&rx_message, &tx_message);
break;
case REQ_ADD_SERVER2:
case REQ_ADD_SERVER3:
handle_add_source(NTP_SERVER, &rx_message, &tx_message);
break;
case REQ_ADD_PEER2:
case REQ_ADD_PEER3:
handle_add_source(NTP_PEER, &rx_message, &tx_message);
break;
@@ -1626,8 +1647,16 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
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;
default:
DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command);
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}

View File

@@ -48,9 +48,10 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
src->params.online = 1;
src->params.connectivity = SRC_ONLINE;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.burst = 0;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
@@ -58,12 +59,15 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.authkey = INACTIVE_AUTHKEY;
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.min_delay = 0.0;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
src->params.offset = 0.0;
hostname = line;
@@ -82,10 +86,12 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0;
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
@@ -98,6 +104,12 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY)
return 0;
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
@@ -116,6 +128,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "mindelay")) {
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
return 0;

158
conf.c
View File

@@ -79,7 +79,7 @@ static void parse_tempcomp(char *);
static int restarted = 0;
static char *rtc_device;
static int acquisition_port = -1;
static int ntp_port = 123;
static int ntp_port = NTP_PORT;
static char *keys_file = NULL;
static char *drift_file = NULL;
static char *rtc_file = NULL;
@@ -97,13 +97,13 @@ static double combine_limit = 3.0;
static int cmd_port = DEFAULT_CANDM_PORT;
static int raw_measurements = 0;
static int do_log_measurements = 0;
static int do_log_statistics = 0;
static int do_log_tracking = 0;
static int do_log_rtc = 0;
static int do_log_refclocks = 0;
static int do_log_tempcomp = 0;
static int do_dump_on_exit = 0;
static int log_banner = 32;
static char *logdir;
static char *dumpdir;
@@ -223,13 +223,7 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
typedef struct {
char *name;
double tx_comp;
double rx_comp;
} HwTs_Interface;
/* Array of HwTs_Interface */
/* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces;
typedef struct {
@@ -276,7 +270,7 @@ static const char *processed_command;
static void
command_parse_error(void)
{
LOG_FATAL(LOGF_Configure, "Could not parse %s directive at line %d%s%s",
LOG_FATAL("Could not parse %s directive at line %d%s%s",
processed_command, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
}
@@ -286,7 +280,7 @@ command_parse_error(void)
static void
other_parse_error(const char *message)
{
LOG_FATAL(LOGF_Configure, "%s at line %d%s%s",
LOG_FATAL("%s at line %d%s%s",
message, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
}
@@ -319,7 +313,7 @@ check_number_of_args(char *line, int num)
num -= get_number_of_args(line);
if (num) {
LOG_FATAL(LOGF_Configure, "%s arguments for %s directive at line %d%s%s",
LOG_FATAL("%s arguments for %s directive at line %d%s%s",
num > 0 ? "Missing" : "Too many",
processed_command, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
@@ -329,11 +323,11 @@ check_number_of_args(char *line, int num)
/* ================================================== */
void
CNF_Initialise(int r)
CNF_Initialise(int r, int client_only)
{
restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (HwTs_Interface));
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
@@ -345,11 +339,18 @@ CNF_Initialise(int r)
dumpdir = Strdup("");
logdir = Strdup("");
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup(DEFAULT_PID_FILE);
rtc_device = Strdup(DEFAULT_RTC_DEVICE);
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER);
if (client_only) {
cmd_port = ntp_port = 0;
bind_cmd_path = Strdup("");
pidfile = Strdup("");
} else {
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup(DEFAULT_PID_FILE);
}
}
/* ================================================== */
@@ -360,7 +361,7 @@ CNF_Finalise(void)
unsigned int i;
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
Free(((HwTs_Interface *)ARR_GetElement(hwts_interfaces, i))->name);
Free(((CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, i))->name);
ARR_DestroyInstance(hwts_interfaces);
for (i = 0; i < ARR_GetSize(ntp_sources); i++)
@@ -403,12 +404,12 @@ CNF_ReadFile(const char *filename)
in = fopen(filename, "r");
if (!in) {
LOG_FATAL(LOGF_Configure, "Could not open configuration file %s : %s",
LOG_FATAL("Could not open configuration file %s : %s",
filename, strerror(errno));
return;
}
DEBUG_LOG(LOGF_Configure, "Reading %s", filename);
DEBUG_LOG("Reading %s", filename);
for (i = 1; fgets(line, sizeof(line), in); i++) {
CNF_ParseLine(filename, i, line);
@@ -474,7 +475,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "dumpdir")) {
parse_string(p, &dumpdir);
} else if (!strcasecmp(command, "dumponexit")) {
do_dump_on_exit = parse_null(p);
/* Silently ignored */
} else if (!strcasecmp(command, "fallbackdrift")) {
parse_fallbackdrift(p);
} else if (!strcasecmp(command, "hwclockfile")) {
@@ -574,7 +575,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
!strcasecmp(command, "generatecommandkey") ||
!strcasecmp(command, "linux_freq_scale") ||
!strcasecmp(command, "linux_hz")) {
LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command);
LOG(LOGS_WARN, "%s directive is no longer supported", command);
} else {
other_parse_error("Invalid command");
}
@@ -680,9 +681,9 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int max_lock_age;
int max_lock_age, pps_forced, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
unsigned char ref[5];
RefclockParameters *refclock;
@@ -690,6 +691,7 @@ parse_refclock(char *line)
poll = 4;
dpoll = 0;
filter_length = 64;
pps_forced = 0;
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
max_samples = SRC_DEFAULT_MAXSAMPLES;
@@ -698,9 +700,12 @@ parse_refclock(char *line)
delay = 1e-9;
precision = 0.0;
max_dispersion = 0.0;
pulse_width = 0.0;
ref_id = 0;
max_lock_age = 2;
lock_ref_id = 0;
stratum = 0;
tai = 0;
if (!*line) {
command_parse_error();
@@ -762,12 +767,25 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "delay")) {
if (sscanf(line, "%lf%n", &delay, &n) != 1)
break;
} else if (!strcasecmp(cmd, "pps")) {
n = 0;
pps_forced = 1;
} else if (!strcasecmp(cmd, "precision")) {
if (sscanf(line, "%lf%n", &precision, &n) != 1)
break;
} else if (!strcasecmp(cmd, "maxdispersion")) {
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)
break;
} else if (!strcasecmp(cmd, "tai")) {
n = 0;
tai = 1;
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
} else if (!strcasecmp(cmd, "noselect")) {
n = 0;
sel_options |= SRC_SELECT_NOSELECT;
@@ -797,14 +815,18 @@ parse_refclock(char *line)
refclock->driver_poll = dpoll;
refclock->poll = poll;
refclock->filter_length = filter_length;
refclock->pps_forced = pps_forced;
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
refclock->max_samples = max_samples;
refclock->sel_options = sel_options;
refclock->stratum = stratum;
refclock->tai = tai;
refclock->offset = offset;
refclock->delay = delay;
refclock->precision = precision;
refclock->max_dispersion = max_dispersion;
refclock->pulse_width = pulse_width;
refclock->ref_id = ref_id;
refclock->max_lock_age = max_lock_age;
refclock->lock_ref_id = lock_ref_id;
@@ -820,7 +842,10 @@ parse_log(char *line)
log_name = line;
line = CPS_SplitWord(line);
if (*log_name) {
if (!strcmp(log_name, "measurements")) {
if (!strcmp(log_name, "rawmeasurements")) {
do_log_measurements = 1;
raw_measurements = 1;
} else if (!strcmp(log_name, "measurements")) {
do_log_measurements = 1;
} else if (!strcmp(log_name, "statistics")) {
do_log_statistics = 1;
@@ -880,7 +905,7 @@ parse_initstepslew(char *line)
if (DNS_Name2IPAddress(hostname, &ip_addr, 1) == DNS_Success) {
ARR_AppendElement(init_sources, &ip_addr);
} else {
LOG(LOGS_WARN, LOGF_Configure, "Could not resolve address of initstepslew server %s", hostname);
LOG(LOGS_WARN, "Could not resolve address of initstepslew server %s", hostname);
}
}
}
@@ -1057,7 +1082,7 @@ parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
}
} else {
if (DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
if (!slashpos && DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
new_node->allow = allow;
new_node->all = all;
@@ -1172,7 +1197,7 @@ parse_broadcast(char *line)
}
} else {
/* default port */
port = 123;
port = NTP_PORT;
}
destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts);
@@ -1251,8 +1276,8 @@ parse_tempcomp(char *line)
static void
parse_hwtimestamp(char *line)
{
HwTs_Interface *iface;
char *p;
CNF_HwTsInterface *iface;
char *p, filter[5];
int n;
if (!*line) {
@@ -1265,18 +1290,50 @@ parse_hwtimestamp(char *line)
iface = ARR_GetNewElement(hwts_interfaces);
iface->name = Strdup(p);
iface->minpoll = 0;
iface->min_samples = 2;
iface->max_samples = 16;
iface->nocrossts = 0;
iface->rxfilter = CNF_HWTS_RXFILTER_ANY;
iface->precision = 100.0e-9;
iface->tx_comp = 0.0;
iface->rx_comp = 0.0;
for (p = line; *p; line += n, p = line) {
line = CPS_SplitWord(line);
if (!strcasecmp(p, "rxcomp")) {
if (!strcasecmp(p, "maxsamples")) {
if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
break;
} else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
} else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
break;
} else if (!strcasecmp(p, "precision")) {
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
break;
} else if (!strcasecmp(p, "rxcomp")) {
if (sscanf(line, "%lf%n", &iface->rx_comp, &n) != 1)
break;
} else if (!strcasecmp(p, "txcomp")) {
if (sscanf(line, "%lf%n", &iface->tx_comp, &n) != 1)
break;
} else if (!strcasecmp(p, "rxfilter")) {
if (sscanf(line, "%4s%n", filter, &n) != 1)
break;
if (!strcasecmp(filter, "none"))
iface->rxfilter = CNF_HWTS_RXFILTER_NONE;
else if (!strcasecmp(filter, "ntp"))
iface->rxfilter = CNF_HWTS_RXFILTER_NTP;
else if (!strcasecmp(filter, "all"))
iface->rxfilter = CNF_HWTS_RXFILTER_ALL;
else
break;
} else if (!strcasecmp(p, "nocrossts")) {
n = 0;
iface->nocrossts = 1;
} else {
break;
}
@@ -1293,11 +1350,19 @@ parse_include(char *line)
{
glob_t gl;
size_t i;
int r;
check_number_of_args(line, 1);
if (glob(line, 0, NULL, &gl)) {
DEBUG_LOG(LOGF_Configure, "glob of %s failed", line);
if ((r = glob(line,
#ifdef GLOB_NOMAGIC
GLOB_NOMAGIC |
#endif
GLOB_ERR, NULL, &gl)) != 0) {
if (r != GLOB_NOMATCH)
LOG_FATAL("Could not search for files matching %s", line);
DEBUG_LOG("glob of %s failed", line);
return;
}
@@ -1323,7 +1388,7 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
existed. It MUST NOT be accessible by others as permissions on Unix
domain sockets are ignored on some systems (e.g. Solaris). */
if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) {
LOG(LOGS_WARN, LOGF_Configure, "Disabled command socket %s", bind_cmd_path);
LOG(LOGS_WARN, "Disabled command socket %s", bind_cmd_path);
bind_cmd_path[0] = '\0';
}
@@ -1354,7 +1419,7 @@ CNF_AddInitSources(void)
ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i);
ntp_addr.port = cps_source.port;
cps_source.params.iburst = 1;
cps_source.params.online = 0;
cps_source.params.connectivity = SRC_OFFLINE;
NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params);
}
@@ -1462,8 +1527,9 @@ CNF_GetDumpDir(void)
/* ================================================== */
int
CNF_GetLogMeasurements(void)
CNF_GetLogMeasurements(int *raw)
{
*raw = raw_measurements;
return do_log_measurements;
}
@@ -1541,14 +1607,6 @@ CNF_GetRtcDevice(void)
/* ================================================== */
int
CNF_GetDumpOnExit(void)
{
return do_dump_on_exit;
}
/* ================================================== */
double
CNF_GetMaxUpdateSkew(void)
{
@@ -1729,7 +1787,7 @@ CNF_SetupAccessRestrictions(void)
node = ARR_GetElement(ntp_restrictions, i);
status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
if (!status) {
LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
}
}
@@ -1737,7 +1795,7 @@ CNF_SetupAccessRestrictions(void)
node = ARR_GetElement(cmd_restrictions, i);
status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
if (!status) {
LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
}
}
@@ -1968,17 +2026,11 @@ CNF_GetInitStepThreshold(void)
/* ================================================== */
int
CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp)
CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
{
HwTs_Interface *iface;
if (index >= ARR_GetSize(hwts_interfaces))
return 0;
iface = ARR_GetElement(hwts_interfaces, index);
*name = iface->name;
*tx_comp = iface->tx_comp;
*rx_comp = iface->rx_comp;
*iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index);
return 1;
}

26
conf.h
View File

@@ -31,7 +31,7 @@
#include "addressing.h"
#include "reference.h"
extern void CNF_Initialise(int restarted);
extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void);
extern char *CNF_GetRtcDevice(void);
@@ -52,7 +52,7 @@ extern char *CNF_GetDriftFile(void);
extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(void);
extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
@@ -60,7 +60,6 @@ extern int CNF_GetLogRefclocks(void);
extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void);
extern int CNF_GetDumpOnExit(void);
extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void);
extern int CNF_GetRtcOnUtc(void);
@@ -119,6 +118,25 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
extern int CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp);
typedef enum {
CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter;
typedef struct {
char *name;
int minpoll;
int min_samples;
int max_samples;
int nocrossts;
CNF_HwTs_RxFilter rxfilter;
double precision;
double tx_comp;
double rx_comp;
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
#endif /* GOT_CONF_H */

160
configure vendored
View File

@@ -5,7 +5,8 @@
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2018
# Copyright (C) Stefan R. Filipek 2019
#
# =======================================================================
@@ -85,6 +86,7 @@ For better control, use the options below.
--with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-cmdmon Disable command and monitoring support
@@ -107,7 +109,7 @@ For better control, use the options below.
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support
@@ -198,6 +200,7 @@ feat_readline=1
try_readline=1
try_editline=1
feat_sechash=1
try_nettle=1
try_nss=1
try_tomcrypt=1
feat_rtc=1
@@ -225,9 +228,10 @@ 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/chronyd.pid"
default_pidfile="/var/run/chrony/chronyd.pid"
default_rtcdevice="/dev/rtc"
mail_program="/usr/lib/sendmail"
@@ -360,6 +364,9 @@ do
--disable-sechash )
feat_sechash=0
;;
--without-nettle )
try_nettle=0
;;
--without-nss )
try_nss=0
;;
@@ -390,7 +397,7 @@ SYSTEM=${OPERATINGSYSTEM}-${MACHINE}
case $OPERATINGSYSTEM in
Linux)
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o"
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o sys_posix.o"
[ $try_libcap != "0" ] && try_libcap=1
try_rtc=1
[ $try_seccomp != "0" ] && try_seccomp=1
@@ -405,7 +412,9 @@ case $OPERATINGSYSTEM in
# recvmmsg() seems to be broken on FreeBSD 11.0 and it's just
# a wrapper around recvmsg()
try_recvmmsg=0
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
try_setsched=1
try_lockmem=1
add_def FREEBSD
if [ $feat_droproot = "1" ]; then
add_def FEAT_PRIVDROP
@@ -414,8 +423,10 @@ case $OPERATINGSYSTEM in
echo "Configuring for $SYSTEM"
;;
NetBSD)
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
try_clockctl=1
try_setsched=1
try_lockmem=1
add_def NETBSD
echo "Configuring for $SYSTEM"
;;
@@ -428,12 +439,23 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
fi
major=`echo $VERSION | cut -d. -f1`
# ntp_adjtime is not available in macOS 10.12 (Darwin 16.x.x) and earlier
if [ $major -gt "16" ]; then
add_def HAVE_MACOS_SYS_TIMEX
EXTRA_OBJECTS="$EXTRA_OBJECTS sys_generic.o sys_netbsd.o sys_timex.o"
if [ $feat_droproot = "1" ]; then
priv_ops="$priv_ops ADJUSTTIMEX"
fi
fi
echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
EXTRA_LIBS="-lsocket -lnsl -lresolv"
EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
try_setsched=1
try_lockmem=1
add_def SOLARIS
# These are needed to have msg_control in struct msghdr
add_def __EXTENSIONS__
@@ -490,14 +512,16 @@ MYCPPFLAGS="$CPPFLAGS"
MYLDFLAGS="$LDFLAGS"
if [ "x$MYCC" = "x" ]; then
MYCC=gcc
if ! test_code "$MYCC" '' '' '' ''; then
MYCC=cc
if ! test_code "$MYCC" '' '' '' ''; then
for cc in gcc clang cc ""; do
if [ "x$cc" = "x" ]; then
echo "error: no C compiler found"
exit 1
fi
fi
MYCC=$cc
if test_code "$MYCC" '' '' '' ''; then
break
fi
done
else
if ! test_code "$MYCC" '' '' '' ''; then
echo "error: C compiler $MYCC cannot create executables"
@@ -507,9 +531,25 @@ fi
if [ "x$MYCFLAGS" = "x" ]; then
MYCFLAGS="-O2 -g"
TESTCFLAGS="-D_FORTIFY_SOURCE=2 -fPIE"
TESTLDFLAGS="-pie -Wl,-z,relro,-z,now"
if test_code 'hardening compiler options' '' "$TESTCFLAGS" "$TESTLDFLAGS" ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
MYLDFLAGS="$MYLDFLAGS $TESTLDFLAGS"
fi
TESTCFLAGS="-fstack-protector-strong --param=ssp-buffer-size=4"
if test_code '-fstack-protector-strong' '' "$TESTCFLAGS" '' ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
else
TESTCFLAGS="-fstack-protector --param=ssp-buffer-size=4"
if test_code '-fstack-protector' '' "$TESTCFLAGS" '' ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
fi
fi
fi
if [ "x$MYCC" = "xgcc" ]; then
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
@@ -523,7 +563,11 @@ then
split_seconds=$ntp_era_split
split_days=0
else
split_seconds=`date '+%s'`
if [ "x$SOURCE_DATE_EPOCH" != "x" ]; then
split_seconds=$SOURCE_DATE_EPOCH
else
split_seconds=`date '+%s'`
fi
if [ "x$split_seconds" = "x" ]; then
echo "error: could not get current time, --with-ntp-era option is needed"
exit 1
@@ -559,14 +603,6 @@ else
fi
fi
if test_code '<stdint.h>' 'stdint.h' '' '' ''; then
add_def HAVE_STDINT_H
fi
if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
add_def HAVE_INTTYPES_H
fi
if test_code 'struct in_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
struct in_pktinfo ipi;
return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;'
@@ -619,18 +655,23 @@ fi
if [ $feat_asyncdns = "1" ] && \
test_code 'pthread' 'pthread.h' '-pthread' '' \
'return pthread_create((void *)1, NULL, (void *)1, NULL);'
'return (int)pthread_create((void *)1, NULL, (void *)1, NULL);'
then
add_def FEAT_ASYNCDNS
add_def USE_PTHREAD_ASYNCDNS
EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
MYCFLAGS="$MYCFLAGS -pthread"
use_pthread=1
fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
add_def HAVE_ARC4RANDOM
fi
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then
add_def HAVE_GETRANDOM
fi
RECVMMSG_CODE='
struct mmsghdr hdr;
return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
@@ -658,15 +699,27 @@ if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
then
add_def HAVE_LINUX_TIMESTAMPING
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o"
if test_code 'other timestamping options' \
'sys/types.h sys/socket.h linux/net_tstamp.h' '' '' '
struct scm_ts_pktinfo pktinfo;
pktinfo.if_index = pktinfo.pkt_length = 0;
return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
SCM_TIMESTAMPING_PKTINFO +
SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then
add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW 1
fi
fi
timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
if test_code '<sys/timepps.h>' 'inttypes.h time.h sys/timepps.h' '' '' ''; then
timepps_h="sys/timepps.h"
add_def HAVE_SYS_TIMEPPS_H
else
if test_code '<timepps.h>' 'timepps.h' '' '' ''; then
if test_code '<timepps.h>' 'inttypes.h time.h timepps.h' '' '' ''; then
timepps_h="timepps.h"
add_def HAVE_TIMEPPS_H
fi
@@ -674,7 +727,7 @@ if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
fi
if [ "x$timepps_h" != "x" ] && \
test_code 'PPSAPI' "string.h $timepps_h" '' '' '
test_code 'PPSAPI' "inttypes.h string.h time.h $timepps_h" '' '' '
pps_handle_t h = 0;
pps_info_t i;
struct timespec ts;
@@ -709,7 +762,7 @@ then
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper
# process works on one request at the time and the async resolver could
# block the main thread
priv_ops="NAME2IPADDRESS"
priv_ops="NAME2IPADDRESS RELOADDNS"
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi
@@ -732,32 +785,42 @@ fi
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
'ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);'
then
grep 'HAVE_LINUX_TIMESTAMPING' config.h > /dev/null ||
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o"
add_def FEAT_PHC
fi
if [ $try_setsched = "1" ] && \
test_code \
'sched_setscheduler()' \
'sched.h' '' '' '
'pthread_setschedparam()' \
'pthread.h sched.h' '-pthread' '' '
struct sched_param sched;
sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &sched);'
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
then
add_def HAVE_SCHED_SETSCHEDULER
add_def HAVE_PTHREAD_SETSCHEDPARAM
use_pthread=1
fi
if [ $try_lockmem = "1" ] && \
test_code \
'mlockall()' \
'sys/mman.h sys/resource.h' '' '' '
struct rlimit rlim;
setrlimit(RLIMIT_MEMLOCK, &rlim);
'sys/mman.h' '' '' '
mlockall(MCL_CURRENT|MCL_FUTURE);'
then
add_def HAVE_MLOCKALL
fi
if [ $try_lockmem = "1" ] && \
test_code \
'setrlimit(RLIMIT_MEMLOCK, ...)' \
'sys/resource.h' '' '' '
struct rlimit rlim;
setrlimit(RLIMIT_MEMLOCK, &rlim);'
then
add_def HAVE_SETRLIMIT_MEMLOCK
fi
if [ $feat_forcednsretry = "1" ]
then
@@ -807,7 +870,22 @@ fi
HASH_OBJ="hash_intmd5.o"
HASH_LINK=""
if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ]; then
test_cflags="`pkg_config --cflags nettle`"
test_link="`pkg_config --libs nettle`"
if test_code 'nettle' 'nettle/nettle-meta.h nettle/sha2.h' \
"$test_cflags" "$test_link" \
'return nettle_hashes[0]->context_size;'
then
HASH_OBJ="hash_nettle.o"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
@@ -834,6 +912,10 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
fi
fi
if [ $use_pthread = "1" ]; then
MYCFLAGS="$MYCFLAGS -pthread"
fi
SYSCONFDIR=/etc
if [ "x$SETSYSCONFDIR" != "x" ]; then
SYSCONFDIR=$SETSYSCONFDIR
@@ -897,9 +979,9 @@ add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features IPV6 DEBUG`"
common_features="`get_features SECHASH IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH SIGND ASYNCDNS`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`"
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

@@ -2,6 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2017
//
// This program is free software; you can redistribute it and/or modify
@@ -65,30 +66,47 @@ server, or its IP address. The *server* directive supports the following
options:
+
*minpoll* _poll_:::
Although *chronyd* will trim the rate at which it samples the server during
normal operation, the user might want to constrain the minimum polling interval.
This is always defined as a power of 2, so *minpoll 5* would mean that the
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds).
This option specifies the minimum interval between requests sent to the server
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
polling interval should not drop below 32 seconds. The default is 6 (64
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
months). Note that intervals shorter than 6 (64 seconds) should generally not
be used with public servers on the Internet, because it might be considered
abuse. A sub-second interval will be enabled only when the server is reachable
and the round-trip delay is shorter than 10 milliseconds, i.e. the server
should be in a local network.
*maxpoll* _poll_:::
In a similar way, the user might want to constrain the maximum polling interval.
Again this is specified as a power of 2, *maxpoll 9* indicates that the polling
interval must stay at or below 512 seconds. The default is 10 (1024 seconds).
This option specifies the maximum interval between requests sent to the server
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
months).
*iburst*:::
If this option is set, the interval between the first four polls will be 2
seconds instead of _minpoll_. This is useful to quickly get the first update of
the clock after *chronyd* is started.
*key* _id_:::
The NTP protocol supports the inclusion of checksums in the packets, to prevent
With this option, the interval between the first four requests sent to the
server will be 2 seconds or less instead of the interval specified by the
*minpoll* option, which allows *chronyd* to make the first update of the clock
shortly after start.
*burst*:::
With this option, *chronyd* will shorten the interval between up to four
requests to 2 seconds or less when it cannot get a good measurement from the
server. The number of requests in the burst is limited by the current polling
interval to keep the average interval at or above the minimum interval, i.e.
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.
The checksums are generated as a function of a password, using the
cryptographic hash function set in the key file, which is specified by the
<<keyfile,*keyfile*>> directive.
The MAC is generated as a function of a password specified in the key file,
which is specified by the <<keyfile,*keyfile*>> directive.
+
If the key option is present, *chronyd* will attempt to use authenticated
packets when communicating with this server. The key number used will be the
single argument to the key option (an unsigned integer in the range 1 through
2^32-1). The server must have the same password for this key number configured,
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,
otherwise no relationship between the computers will be possible.
+
If the server is running *ntpd* and the output size of the hash function used
by the key is longer than 160 bits (e.g. SHA256), the *version* option needs to
be set to 4 for compatibility.
*maxdelay* _delay_:::
*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
@@ -108,16 +126,28 @@ option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is
3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_:::
This option is similar to the maxdelay option above. *chronyd* keeps a record
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
of the minimum round-trip delay amongst the previous measurements that it has
buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected. This option works
only in the *server* directive when not in the interleaved mode.
maxdelayratio times the minimum delay, it will be rejected.
*maxdelaydevratio* _ratio_:::
If a measurement has a ratio of the increase in the round-trip delay from the
minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0.
*mindelay* _delay_:::
This option specifies a fixed minimum round-trip delay to be used instead of
the minimum amongst the previous measurements. This can be useful in networks
with static configuration to improve the stability of corrections for
asymmetric jitter, weighting of the measurements, and the *maxdelayratio* and
*maxdelaydevratio* tests. The value should be set accurately in order to have a
positive effect on the synchronisation.
*asymmetry* _ratio_:::
This option specifies the asymmetry of the network jitter on the path to the
source, which is used to correct the measured offset according to the delay.
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
offsets measured with this source. It's particularly useful to compensate for a
@@ -131,20 +161,25 @@ Set the minimum number of samples kept for this source. This overrides the
*maxsamples* _samples_:::
Set the maximum number of samples kept for this source. This overrides the
<<maxsamples,*maxsamples*>> directive.
*filter* _samples_:::
This option enables a median filter to reduce noise in NTP measurements. The
filter will reduce the specified number of samples to a single sample. It is
intended to be used with very short polling intervals in local networks where
it is acceptable to generate a lot of NTP traffic.
*offline*:::
If the server will not be reachable when *chronyd* is started, the *offline*
option can be specified. *chronyd* will not try to poll the server until it is
enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
*chronyc*).
*auto_offline*:::
If this option is set, the server will be assumed to have gone offline when 2
requests have been sent to it without receiving a response. This option avoids
With this option, the server will be assumed to have gone offline when sending
a request fails, e.g. due to a missing route to the network. This option avoids
the need to run the <<chronyc.adoc#offline,*offline*>> command from *chronyc*
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 prefer option.
Prefer this source over sources without the *prefer* option.
*noselect*:::
Never select this source. This is particularly useful for monitoring.
*trust*:::
@@ -172,9 +207,11 @@ the interleaved mode requires the servers to keep some state for each client
and the state might be dropped when there are too many clients (e.g.
<<clientloglimit,*clientloglimit*>> is too small), or it might be overwritten
by other clients that have the same IP address (e.g. computers behind NAT or
someone sending requests with a spoofed source address). The *presend* option
can be used to shorten the interval in which the server has to keep the state
for this computer and be able to respond in the interleaved mode.
someone sending requests with a spoofed source address).
+
The *xleave* option can be combined with the *presend* option in order to
shorten the interval in which the server has to keep the state to be able to
respond in the interleaved mode.
*polltarget* _target_:::
Target number of measurements to use for the regression algorithm which
*chronyd* will try to maintain by adjusting the polling interval between
@@ -337,47 +374,56 @@ for *initstepslew* to finish before exiting. This is useful to prevent programs
started in the boot sequence after *chronyd* from reading the clock before it
has been stepped.
[[refclock]]*refclock* _driver_ _parameter_ [_option_]...::
[[refclock]]*refclock* _driver_ _parameter_[:__option__]... [_option_]...::
The *refclock* directive specifies a hardware reference clock to be used as a
time source. It has two mandatory parameters, a driver name and a
driver-specific parameter.
driver-specific parameter. The two parameters are followed by zero or more
refclock options. Some drivers have special options, which can be appended to
the driver-specific parameter using the *:* character.
+
There are currently four drivers included:
There are four drivers included in *chronyd*:
+
*PPS*:::
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
the PPS device (typically _/dev/pps?_). The assert events are used for
synchronisation by default. String *:clear* can be appended to the path to use
the clear events instead.
the PPS device (typically _/dev/pps?_). As PPS refclocks do not supply full
time, another time source (e.g. NTP server or non-PPS refclock) is needed to
complete samples from the PPS refclock. An alternative is to enable the
<<local,*local*>> directive to allow synchronisation with some unknown but
constant offset. The driver supports the following option:
+
As PPS refclocks don't supply full time, *chronyd* needs to be configured with
another time source (NTP or non-PPS refclock) in order to complete samples from
the PPS refclock. An alternative is to enable the <<local,*local*>> directive
to allow synchronisation with some unknown but constant offset.
For example:
*clear*::::
By default, the PPS refclock uses assert events (rising edge) for
synchronisation. With this option, it will use clear events (falling edge)
instead.
+
:::
Examples:
+
----
refclock PPS /dev/pps0 lock NMEA
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
----
+
*SHM*:::
NTP shared memory driver. This driver uses a shared memory segment to receive
samples from another process. The parameter is the number of the shared memory
segment, typically 0, 1, 2 or 3. For example:
samples from another process (e.g. *gpsd*). The parameter is the number of the
shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
supports the following option:
+
*perm*=_mode_::::
This option specifies the permissions of the shared memory segment created by
*chronyd*. They are specified as a numeric mode. The default value is 0600
(read-write access for owner only).
:::
+
Examples:
+
----
refclock SHM 1 poll 3 refid GPS1
refclock SHM 0 poll 3 refid GPS1
refclock SHM 1:perm=0644 refid GPS2
----
+
A driver option in form of *:perm=NNN* can be appended to the segment number to
create the segment with permissions other than the default 0600.
+
Examples of applications that can be used as SHM refclocks are
http://www.catb.org/gpsd/[*gpsd*],
http://www.buzzard.me.uk/jonathan/radioclock.html[*radioclk*], and
https://www.vanheusden.com/time/omnisync/[*omnisync*].
+
*SOCK*:::
Unix domain socket driver. It is similar to the SHM driver, but samples are
received from a Unix domain socket instead of shared memory and the messages
@@ -397,17 +443,44 @@ refclock SOCK /var/run/chrony.ttyS0.sock
+
*PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of
the PTP clock, which for example can be synchronised by *ptp4l* from
http://linuxptp.sourceforge.net[*linuxptp*]. PTP clocks are typically kept in
TAI instead of UTC, so the *offset* option should be used to compensate for the
current UTC-TAI offset. For example:
the PTP clock which should be used as a time source. 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
only the sub-second offset for synchronisation. The driver supports the
following options:
+
*nocrossts*::::
This option disables use of precise cross timestamping.
*extpps*::::
This option enables a PPS mode in which the PTP clock is timestamping pulses
of an external PPS signal connected to the clock. The clock does not need to be
synchronised, but another time source is needed to complete the PPS samples.
Note that some PTP clocks cannot be configured to timestamp only assert or
clear events, and it is necessary to use the *width* option to filter wrong
PPS samples.
*pin*=_index_::::
This option specifies the index of the pin to which is connected the PPS
signal. The default value is 0.
*channel*=_index_::::
This option specifies the index of the channel for the PPS mode. The default
value is 0.
*clear*::::
This option enables timestamping of clear events (falling edge) instead of
assert events (rising edge) in the PPS mode. This may not work with some
clocks.
:::
+
Examples:
+
----
refclock PHC /dev/ptp0 poll 3 dpoll -2 offset -36
refclock PHC /dev/ptp0 poll 0 dpoll -2 offset -37
refclock PHC /dev/ptp1:nocrossts poll 3 pps
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
----
+
::
The *refclock* directive also supports a number of options:
The *refclock* directive supports the following options:
+
*poll* _poll_:::
Timestamps produced by refclock drivers are not used immediately, but they are
@@ -440,6 +513,17 @@ This option specifies in number of pulses how old can be samples 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
completes the PPS samples. If the duty cycle is configurable, 50% should be
preferred in order to maximise the allowed error.
*pps*:::
This options forces *chronyd* to treat any refclock (e.g. SHM or PHC) as a PPS
refclock. This can be useful when the refclock provides time with a variable
offset of a whole number of seconds (e.g. it uses TAI instead of UTC). Another
time source is needed to complete samples from the refclock.
*offset* _offset_:::
This option can be used to compensate for a constant error. The specified
offset (in seconds) is applied to all samples produced by the reference clock.
@@ -450,10 +534,12 @@ 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).
*stratum* _stratum_:::
This option sets the NTP stratum of the refclock. This can be useful when the
refclock provides time with a stratum other than 0. The default is 0.
*precision* _precision_:::
This option sets the refclock precision (in seconds). The default is 1e-6 (1
microsecond) for SHM refclock, and 1e-9 (1 nanosecond) for SOCK, PPS and PHC
refclocks.
This option sets the precision of the reference clock (in seconds). The default
value is the estimated precision of the system clock.
*maxdispersion* _dispersion_:::
Maximum allowed dispersion for filtered samples (in seconds). Samples with
larger estimated dispersion are ignored. By default, this limit is disabled.
@@ -481,6 +567,12 @@ but not very precise, reference clock to be safely combined with
unauthenticated NTP sources in order to improve the accuracy of the clock. They
can be selected and used for synchronisation only if they agree with the
trusted and required source.
*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.
*minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
@@ -534,19 +626,18 @@ can be specified.
To compute the rate of gain or loss of time, *chronyd* has to store a
measurement history for each of the time sources it uses.
+
Certain systems (Linux, FreeBSD, NetBSD, Solaris) have operating system support
for setting the rate of gain or loss to compensate for known errors. (On Mac OS
X, *chronyd* must simulate such a capability by periodically slewing the system
clock forwards or backwards by a suitable amount to compensate for the error
built up since the previous slew.)
All supported systems, with the exception of macOS 10.12 and earlier, have
operating system support for setting the rate of gain or loss to compensate for
known errors.
(On macOS 10.12 and earlier, *chronyd* must simulate such a capability by
periodically slewing the system clock forwards or backwards by a suitable amount
to compensate for the error built up since the previous slew.)
+
For such systems, it is possible to save the measurement history across
restarts of *chronyd* (assuming no changes are made to the system clock
behaviour whilst it is not running). If this capability is to be used (via the
*dumponexit* directive in the configuration file, or the
<<chronyc.adoc#dump,*dump*>> command in *chronyc*), the *dumpdir* directive
should be used to define the directory where the measurement histories are
saved.
behaviour whilst it is not running). The *dumpdir* directive defines the
directory where the measurement histories are saved when *chronyd* exits,
or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
+
An example of the directive is:
+
@@ -558,11 +649,6 @@ A source whose IP address is _1.2.3.4_ would have its measurement history saved
in the file _@CHRONYRUNDIR@/1.2.3.4.dat_. History of reference clocks is saved
to files named by their reference ID in form of _refid:XXXXXXXX.dat_.
[[dumponexit]]*dumponexit*::
If this directive is present, it indicates that *chronyd* should save the
measurement history for each of its time sources recorded whenever the program
exits. (See the <<dumpdir,*dumpdir*>> directive above.)
[[maxsamples]]*maxsamples* _samples_::
The *maxsamples* directive sets the default maximum number of samples that
*chronyd* should keep for each source. This setting can be overridden for
@@ -575,6 +661,13 @@ The *minsamples* directive sets the default minimum number of samples that
*chronyd* should keep for each source. This setting can be overridden for
individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
directives. The default value is 6. The useful range is 4 to 64.
+
Forcing *chronyd* to keep more samples than it would normally keep reduces
noise in the estimated frequency and offset, but slows down the response to
changes in the frequency and offset of the clock. The offsets in the
<<chronyc.adoc#tracking,*tracking*>> and
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
_statistics.log_ files) may be smaller than the actual offsets.
=== Source selection
@@ -588,6 +681,8 @@ The *combinelimit* directive limits which sources are included in the combining
algorithm. Their synchronisation distance has to be shorter than the distance
of the selected source multiplied by the value of the limit. Also, their
measured frequencies have to be close to the frequency of the selected source.
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
@@ -644,8 +739,9 @@ distances are in milliseconds.
[[corrtimeratio]]*corrtimeratio* _ratio_::
When *chronyd* is slewing the system clock to correct an offset, the rate at
which it is slewing adds to the frequency error of the clock. On Linux,
FreeBSD, NetBSD and Solaris this rate can be controlled.
which it is slewing adds to the frequency error of the clock. On all supported
systems, with the exception of macOS 12 and earlier, this rate can be
controlled.
+
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
@@ -691,9 +787,10 @@ driftfile @CHRONYVARDIR@/drift
[[fallbackdrift]]*fallbackdrift* _min-interval_ _max-interval_::
Fallback drifts are long-term averages of the system clock drift calculated
over exponentially increasing intervals. They are used when the clock is no
longer synchronised to avoid quickly drifting away from true time if there was
a short-term deviation in the drift before the synchronisation was lost.
over exponentially increasing intervals. They are used to avoid quickly
drifting away from true time when the clock was not updated for a longer period
of time and there was a short-term deviation in the drift before the updates
stopped.
+
The directive specifies the minimum and maximum interval since the last clock
update to switch between fallback drifts. They are defined as a power of 2 (in
@@ -705,8 +802,10 @@ 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, etc. This might be
a good setting to cover daily and weekly temperature fluctuations.
hours after 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`'.
+
By default (or if the specified maximum or minimum is 0), no fallbacks are used
and the clock frequency changes only with new measurements from NTP sources,
@@ -728,8 +827,8 @@ that error is corrected. There are four options:
When inserting a leap second, the kernel steps the system clock backwards by
one second when the clock gets to 00:00:00 UTC. When deleting a leap second, it
steps forward by one second when the clock gets to 23:59:59 UTC. This is the
default mode when the system driver supports leap seconds (i.e. on Linux,
FreeBSD, NetBSD and Solaris).
default mode when the system driver supports leap seconds (i.e. all supported
systems with the exception of macOS 12 and earlier).
*step*:::
This is similar to the *system* mode, except the clock is stepped by
*chronyd* instead of the kernel. It can be useful to avoid bugs in the kernel
@@ -784,15 +883,28 @@ clients to safely synchronise with multiple identically configured leap
smearing servers.
[[leapsectz]]*leapsectz* _timezone_::
This directive is used to set the name of the timezone in the system tz
database which *chronyd* can use to find out when will the next leap second
occur. It will periodically check if the times 23:59:59 and 23:59:60 are valid
on Jun 30 and Dec 31 in the timezone. This typically works with the *right/UTC*
timezone.
This directive specifies a timezone in the system tz 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 and
23:59:60 are valid times in the timezone. This typically works with the
_right/UTC_ timezone.
+
This directive is mainly useful with reference clocks which do not provide
leap second information. It is not necessary to restart *chronyd* if the tz
database is updated with a new leap second at least 12 hours before the event.
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
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.
+
It is also useful when the system clock is required to have correct TAI-UTC
offset. Note that the offset is set only when leap seconds are handled by the
kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*.
+
The specified timezone is not used as an exclusive source of information about
leap seconds. If a majority of time sources announce on the last day of June or
December that a leap second should be inserted or deleted, it will be accepted
even if it is not included in the timezone.
+
An example of the directive is:
+
@@ -864,7 +976,7 @@ This directive specifies the maximum assumed drift (frequency error) of the
system clock. It limits the frequency adjustment that *chronyd* is allowed to
use to correct the measured drift. It is an additional limit to the maximum
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
on FreeBSD and NetBSD, 32500 ppm on Solaris).
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
+
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
limited by the system driver rather than this directive.
@@ -899,14 +1011,18 @@ The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
to slew the time. It limits the slew rate controlled by the correction time
ratio (which can be set by the <<corrtimeratio,*corrtimeratio*>> directive) and
is effective only on systems where *chronyd* is able to control the rate (i.e.
Linux, FreeBSD, NetBSD, Solaris).
all supported systems with the exception of macOS 12 or earlier).
+
For each system there is a maximum frequency offset of the clock that
can be set by the driver. On Linux it is 100000 ppm, on FreeBSD and NetBSD
it is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel
limitation, setting *maxslewrate* on FreeBSD and NetBSD to a value between 500
For each system there is a maximum frequency offset of the clock that can be set
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
ppm and 5000 ppm will effectively set it to 500 ppm.
+
In early beta releases of macOS 13 this capability is disabled because of a
system kernel bug. When the kernel bug is fixed, chronyd will detect this and
re-enable the capability (see above limitations) with no recompilation required.
+
By default, the maximum slew rate is set to 83333.333 ppm (one twelfth).
[[tempcomp]]
@@ -1487,8 +1603,8 @@ If the *rtconutc* directive appears, it means the RTC is required to keep UTC.
The directive takes no arguments. It is equivalent to specifying the *-u*
switch to the Linux *hwclock* program.
+
Note that this setting is overridden when the <<hwclockfile,*hwclockfile*>>
directive is specified.
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
and is not relevant for the <<rtcsync,*rtcsync*>> directive.
[[rtcsync]]*rtcsync*::
The *rtcsync* directive enables a mode where the system time is periodically
@@ -1510,10 +1626,12 @@ The log files are written to the directory specified by the <<logdir,*logdir*>>
directive. A banner is periodically written to the files to indicate the
meanings of the columns.
+
*measurements*:::
*rawmeasurements*:::
This option logs the raw NTP measurements and related information to a file
called _measurements.log_. An example line (which actually appears as a single
line in the file) from the log file is shown below.
called _measurements.log_. An entry is made for each packet received from the
source. This can be useful when debugging a problem. An example line (which
actually appears as a single line in the file) from the log file is shown
below.
+
----
2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
@@ -1549,12 +1667,18 @@ from the example line above):
. The root dispersion (_EPSILON_ in RFC 5905). [7.446e-03]
. Reference ID of the server's source as a hexadecimal number. [CB00717B]
. NTP mode of the received packet (_1_=active peer, _2_=passive peer,
_3_=server, _B_=basic, _I_=interleaved). [4B]
_4_=server, _B_=basic, _I_=interleaved). [4B]
. Source of the local transmit timestamp
(_D_=daemon, _K_=kernel, _H_=hardware). [D]
. Source of the local receive timestamp
(_D_=daemon, _K_=kernel, _H_=hardware). [K]
+
*measurements*:::
This option is identical to the *rawmeasurements* option, except it logs only
valid measurements from synchronised sources, i.e. measurements which passed
the RFC 5905 tests 1 through 7. This can be useful for producing graphs of the
source's performance.
+
*statistics*:::
This option logs information about the regression processing to a file called
_statistics.log_. An example line (which actually appears as a single line in
@@ -1597,11 +1721,11 @@ from the example line above):
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.
[8]
. The estimated asymmetry of network jitter on the path to the source which was
used to correct the measured offsets. 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. [0.00,
i.e. no correction for asymmetry]
. The estimated or configured asymmetry of network jitter on the path to the
source which was used to correct the measured offsets. 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. [0.00, i.e. no correction for asymmetry]
+
*tracking*:::
This option logs changes to the estimate of the system's gain or loss rate, and
@@ -1610,33 +1734,42 @@ actually appears as a single line in the file) from the log file is shown
below.
+
----
2015-02-23 05:40:50 203.0.113.15 3 340.529 1.606 1.046e-03 N \
4 6.849e-03 -4.670e-04
2017-08-22 13:22:36 203.0.113.15 2 -3.541 0.075 -8.621e-06 N \
2 2.940e-03 -2.084e-04 1.534e-02 3.472e-04 8.304e-03
----
+
The columns are as follows (the quantities in square brackets are the
values from the example line above) :
+
. Date [2015-02-03]
. Date [2017-08-22]
. Hour:Minute:Second. Note that the date-time pair is expressed in UTC, not the
local time zone. [05:40:50]
local time zone. [13:22:36]
. The IP address of the server or peer to which the local system is synchronised.
[203.0.113.15]
. The stratum of the local system. [3]
. The stratum of the local system. [2]
. The local system frequency (in ppm, positive means the local system runs fast
of UTC). [340.529]
. The error bounds on the frequency (in ppm). [1.606]
. The estimated local offset at the epoch (which is rapidly corrected by
slewing the local clock. (In seconds, positive indicates the local system
is fast of UTC). [1.046e-3]
of UTC). [-3.541]
. The error bounds on the frequency (in ppm). [0.075]
. The estimated local offset at the epoch, which is normally corrected by
slewing the local clock (in seconds, positive indicates the clock is fast of
UTC). [-8.621e-06]
. Leap status (_N_ means normal, _+_ means that the last minute of this month
has 61 seconds, _-_ means that the last minute of the month has 59 seconds,
_?_ means the clock is not currently synchronised.) [N]
. The number of combined sources. [4]
. The number of combined sources. [2]
. The estimated standard deviation of the combined offset (in seconds).
[6.849e-03]
[2.940e-03]
. The remaining offset correction from the previous update (in seconds,
positive means the system clock is slow of UTC). [-4.670e-04]
positive means the system clock is slow of UTC). [-2.084e-04]
. The total of the network path delays to the reference clock to which
the local clock is ultimately synchronised (in seconds). [1.534e-02]
. The total dispersion accumulated through all the servers back to the
reference clock to which the local clock is ultimately synchronised
(in seconds). [3.472e-04]
. The maximum estimated error of the system clock in the interval since the
previous update (in seconds). It includes the offset, remaining offset
correction, root delay, and dispersion from the previous update with the
dispersion which accumulated in the interval. [8.304e-03]
+
*rtc*:::
This option logs information about the system's real-time clock. An example
@@ -1797,23 +1930,39 @@ be enabled by the *xleave* option in the <<server,*server*>> or the
This directive is supported on Linux 3.19 and newer. The NIC must support HW
timestamping, which can be verified with the *ethtool -T* command. The list of
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
_SOF_TIMESTAMPING_TX_HARDWARE_, _SOF_TIMESTAMPING_RX_HARDWARE_, and the filter
modes should have _HWTSTAMP_FILTER_ALL_. When *chronyd* is running, no other
process should be working with the clock on the NIC. If no *hwtimestamp*
directive is specified, *chronyd* will try to use software (kernel)
timestamping. With both hardware and software timestamping there are
some limitations on which packets can be actually timestamped, e.g. transmit
timestamping does not currently work with IPv6 packets using IP options and
hardware receive timestamping does not work with packets from bridged
interfaces. The timestamping used in measurements is indicated in the
_measurements.log_ file if enabled by the <<log,*log measurements*>> directive,
and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in *chronyc*.
_SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive
filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for
timestamping of received packets. Timestamping of packets received from bridged
and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is
running, no other process (e.g. a PTP daemon) should be working with the NIC
clock.
+
If the kernel supports software timestamping, it will be enabled for all
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
indicated in the _measurements.log_ file if enabled by the <<log,*log
measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
*chronyc*.
+
If the specified interface is _*_, *chronyd* will try to enable HW timestamping
on all available interfaces.
+
The *hwtimestamp* directive has the following options:
+
*minpoll* _poll_:::
This option specifies the minimum interval between readings of the NIC clock.
It's defined as a power of two. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
of a second).
*minsamples* _samples_:::
This option specifies the minimum number of readings kept for tracking of the
NIC clock. The default value is 2.
*maxsamples* _samples_:::
This option specifies the maximum number of readings kept for tracking of the
NIC clock. The default value is 16.
*precision* _precision_:::
This option specifies the assumed precision of reading of the NIC clock. The
default value is 100e-9 (100 nanoseconds).
*txcomp* _compensation_:::
This option specifies the difference in seconds between the actual transmission
time at the physical layer and the reported transmit timestamp. This value will
@@ -1823,6 +1972,25 @@ This option specifies the difference in seconds between the reported receive
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.
*rxfilter* _filter_:::
This option selects the receive timestamping filter. The _filter_ can be one of
the following:
_all_::::
Enables timestamping of all received packets.
_ntp_::::
Enables timestamping of received NTP packets.
_none_::::
Disables timestamping of received packets.
:::
The most specific filter for timestamping NTP packets which is supported by the
NIC is selected by default. Some NICs can timestamp only PTP packets, which
limits the selection to the _none_ filter. Forcing timestamping of all packets
with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be
useful when packets are received from or on a non-standard UDP port (e.g.
specified by the *port* directive).
::
+
Examples of the directive are:
@@ -1867,12 +2035,18 @@ format of the file is shown below:
+
Each line consists of an ID, name of an authentication hash function (optional),
and a password. The ID can be any unsigned integer in the range 1 through
2^32-1. The default hash function is *MD5*. Depending on how *chronyd*
was compiled, other supported functions might be *SHA1*, *SHA256*, *SHA384*,
*SHA512*, *RMD128*, *RMD160*, *RMD256*, *RMD320*, *TIGER*, and *WHIRLPOOL*. The
password can be specified as a string of characters not containing white space
with an optional *ASCII:* prefix, or as a hexadecimal number with the *HEX:*
prefix. The maximum length of the line is 2047 characters.
2^32-1. The default hash function is *MD5*, which is always supported.
+
If *chronyd* was built with enabled support for hashing using a crypto library
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
*chronyd* using, some or all of the following functions may also be available:
*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *RMD128*, *RMD160*, *RMD256*,
*RMD320*, *TIGER*, *WHIRLPOOL*.
+
The password can be specified as a string of characters not containing white
space with an optional *ASCII:* prefix, or as a hexadecimal number with the
*HEX:* prefix. The maximum length of the line is 2047 characters.
+
The password is used with the hash function to generate and verify a message
authentication code (MAC) in NTP packets. It is recommended to use SHA1, or
@@ -1884,46 +2058,51 @@ that has password shorter than 80 bits.
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
SHA1 keys.
+
For security reasons, the file should be readable only by root and the user
under which *chronyd* is normally running (to allow *chronyd* to re-read the
file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
[[lock_all]]*lock_all*::
The *lock_all* directive will lock chronyd into RAM so that it will never be
paged out. This mode is only supported on Linux. This directive uses the Linux
*mlockall()* system call to prevent *chronyd* from ever being swapped out. This
should result in lower and more consistent latency. It should not have
significant impact on performance as *chronyd's* memory usage is modest. The
*mlockall(2)* man page has more details.
paged out. This mode is supported on Linux, FreeBSD, NetBSD, and Solaris. This
directive uses the POSIX *mlockall()* system call to prevent *chronyd* from
ever being swapped out. This should result in lower and more consistent
latency. It should not have significant impact on performance as *chronyd's*
memory usage is modest. The *mlockall(2)* man page has more details.
[[pidfile]]*pidfile* _file_::
*chronyd* always writes its process ID (PID) to a file, and checks this file on
startup to see if another *chronyd* might already be running on the system. By
default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
allows the name to be changed, e.g.:
Unless *chronyd* is started with the *-Q* option, it writes its process ID
(PID) to a file, and checks this file on startup to see if another *chronyd*
might already be running on the system. By default, the file used is
_@DEFAULT_PID_FILE@_. The *pidfile* directive allows the name to be changed,
e.g.:
+
----
pidfile /run/chronyd.pid
----
[[sched_priority]]*sched_priority* _priority_::
On Linux, the *sched_priority* directive will select the SCHED_FIFO real-time
scheduler at the specified priority (which must be between 0 and 100). On
macOS, this option must have either a value of 0 (the default) to disable the
thread time constraint policy or 1 for the policy to be enabled. Other systems
do not support this option.
On Linux, FreeBSD, NetBSD, and Solaris, the *sched_priority* directive will
select the SCHED_FIFO real-time scheduler at the specified priority (which must
be between 0 and 100). On macOS, this option must have either a value of 0 (the
default) to disable the thread time constraint policy or 1 for the policy to be
enabled.
+
On Linux, this directive uses the *sched_setscheduler()* system call to
instruct the kernel to use the SCHED_FIFO first-in, first-out real-time
scheduling policy for *chronyd* with the specified priority. This means that
whenever *chronyd* is ready to run it will run, interrupting whatever else is
running unless it is a higher priority real-time process. This should not
impact performance as *chronyd* resource requirements are modest, but it should
result in lower and more consistent latency since *chronyd* will not need to
wait for the scheduler to get around to running it. You should not use this
unless you really need it. The *sched_setscheduler(2)* man page has more
details.
On systems other than macOS, this directive uses the *pthread_setschedparam()*
system call to instruct the kernel to use the SCHED_FIFO first-in, first-out
real-time scheduling policy for *chronyd* with the specified priority. This
means that whenever *chronyd* is ready to run it will run, interrupting
whatever else is running unless it is a higher priority real-time process. This
should not impact performance as *chronyd* resource requirements are modest,
but it should result in lower and more consistent latency since *chronyd* will
not need to wait for the scheduler to get around to running it. You should not
use this unless you really need it. The *pthread_setschedparam(3)* man page has
more details.
+
On macOS, this directive uses the *thread_policy_set()* kernel call to
specify real-time scheduling. As noted for Linux, you should not use this
directive unless you really need it.
specify real-time scheduling. As noted above, you should not use this directive
unless you really need it.
[[user]]*user* _user_::
The *user* directive sets the name of the system user to which *chronyd* will
@@ -2150,8 +2329,7 @@ is made of the RTC error at a particular RTC second, and the rate at which the
RTC gains or loses time relative to true time.
When the computer is powered down, the measurement histories for all the NTP
servers are saved to files (if the <<dumponexit,*dumponexit*>> directive is
specified in the configuration file), and the RTC tracking information is also
servers are saved to files, and the RTC tracking information is also
saved to a file (if the <<rtcfile,*rtcfile*>> directive has been specified).
These pieces of information are also saved if the <<chronyc.adoc#dump,*dump*>>
and <<chronyc.adoc#writertc,*writertc*>> commands respectively are issued
@@ -2204,7 +2382,6 @@ log statistics measurements tracking
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
maxupdateskew 100.0
dumponexit
dumpdir @CHRONYVARDIR@
rtcfile @CHRONYVARDIR@/rtc
----
@@ -2248,7 +2425,7 @@ The amount of memory used for logging client accesses can be increased in order
to enable clients to use the interleaved mode even when the server has a large
number of clients, and better support rate limiting if it is enabled by the
<<ratelimit,*ratelimit*>> directive. The system timezone database, if it is
kept up to date and includes the *right/UTC* timezone, can be used as a
kept up to date and includes the _right/UTC_ timezone, can be used as a
reliable source to determine when a leap second will be applied to UTC. The
*-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which
*chronyd* will not be able to serve time to its clients when it needs to be
@@ -2269,7 +2446,6 @@ clientloglimit 100000000
leapsectz right/UTC
driftfile @CHRONYVARDIR@/drift
dumpdir @CHRONYRUNDIR@
dumponexit
----
== SEE ALSO

View File

@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2016
// Copyright (C) Miroslav Lichvar 2009-2017
//
// 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
@@ -130,15 +130,15 @@ performance. An example of the output is shown below.
----
Reference ID : CB00710F (foo.example.net)
Stratum : 3
Ref time (UTC) : Fri Feb 3 15:00:29 2012
System time : 0.000001501 seconds slow of NTP time
Last offset : -0.000001632 seconds
RMS offset : 0.000002360 seconds
Frequency : 331.898 ppm fast
Residual freq : 0.004 ppm
Skew : 0.154 ppm
Root delay : 0.373169 seconds
Root dispersion : 0.024780 seconds
Ref time (UTC) : Fri Jan 27 09:49:17 2017
System time : 0.000006523 seconds slow of NTP time
Last offset : -0.000006747 seconds
RMS offset : 0.000035822 seconds
Frequency : 3.225 ppm slow
Residual freq : -0.000 ppm
Skew : 0.129 ppm
Root delay : 0.013639022 seconds
Root dispersion : 0.001100737 seconds
Update interval : 64.2 seconds
Leap status : Normal
----
@@ -187,9 +187,6 @@ The '`frequency`' is the rate by which the system's clock would be wrong if
For example, a value of 1 ppm would mean that when the system's clock thinks it
has advanced 1 second, it has actually advanced by 1.000001 seconds relative to
true time.
+
As you can see in the example, the clock in the computer is not a very
good one; it would gain about 30 seconds per day if it was not corrected!
*Residual freq*:::
This shows the '`residual frequency`' for the currently selected reference
source. This reflects any difference between what the measurements from the
@@ -219,7 +216,7 @@ An absolute bound on the computer's clock accuracy (assuming the stratum-1
computer is correct) is given by:
+
----
clock_error <= root_dispersion + (0.5 * |root_delay|)
clock_error <= |system_time_offset| + root_dispersion + (0.5 * root_delay)
----
*Update interval*:::
This is the interval between the last two clock updates.
@@ -341,8 +338,9 @@ 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.
*LastRx*:::
This column shows how long ago the last sample was received from the source.
This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
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
ignored. This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
minutes, hours, days, or years.
*Last sample*:::
This column shows the offset between the local clock and the source at the
@@ -725,6 +723,13 @@ particular source or sources has been restored.
+
The syntax is identical to that of the <<offline,*offline*>> command.
[[onoffline]]
*onoffline*::
The *onoffline* command tells *chronyd* to switch all sources to the online or
offline status according to the current network configuration. A source is
considered online if it is possible to send requests to it, i.e. a route to the
network is present.
[[polltarget]]*polltarget* _address_ _polltarget_::
The *polltarget* command is used to modify the poll target for one of the
current set of sources. It is equivalent to the *polltarget* option in the
@@ -1121,17 +1126,19 @@ purged. An example of how to do this is shown below.
[[dump]]*dump*::
The *dump* command causes *chronyd* to write its current history of
measurements for each of its sources to dump files, either for inspection or to
support the *-r* option when *chronyd* is restarted.
+
The *dump* command is somewhat equivalent to the
<<chrony.conf.adoc#dumponexit,*dumponexit*>> directive in the configuration
file.
+
To use the *dump* command, you might want to configure the name of the
directory into which the dump files will be written. This can only be
done in the configuration file with the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive.
measurements for each of its sources to dump files in the directory specified
in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive. Note that *chronyd* does this automatically when it exits. This
command is mainly useful for inspection of the history whilst *chronyd* is
running.
[[rekey]]*rekey*::
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
[[rekey]]*shutdown*::
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
the process the SIGTERM signal.
=== Client commands

View File

@@ -1,7 +1,7 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2016
// Copyright (C) Miroslav Lichvar 2009-2017
//
// 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
@@ -62,28 +62,33 @@ When run in this mode, the program will not detach itself from the terminal.
*-d*::
When run in this mode, the program will not detach itself from the terminal,
and all messages will be sent to the terminal instead of to syslog. When
and all messages will be written to the terminal instead of syslog. When
*chronyd* was compiled with debugging support, this option can be used twice to
print also debugging messages.
*-l* _file_::
This option specifies a file which should be used for logging instead of syslog
or terminal.
*-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal.
*-Q*::
This option is similar to *-q*, but it will only print the offset without any
corrections of the clock.
This option is similar to the *-q* option, except it only prints the offset
without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
*-r*::
This option will try to reload and then delete files containing sample
histories for each of the servers and reference clocks being used. These
histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
*chronyc*, or by setting the <<chrony.conf.adoc#dumponexit,*dumponexit*>>
histories for each of the servers and reference clocks being used. The
files are expected to be in the directory specified by the
<<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD
and Solaris).
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
Solaris, and macOS 10.13 or later).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -130,7 +135,7 @@ range of privileged system calls on behalf of the parent.
*-F* _level_::
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
process is killed when a forbidden system call is made, in level -1 the SYSSIG
process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled (default 0).
+
It's recommended to enable the filter only when it's known to work on the
@@ -151,6 +156,15 @@ support this option.
This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux.
*-x*::
This option disables the control of the system clock. *chronyd* will not try to
make any adjustments of the clock. It will assume the clock is free running and
still track its offset and frequency relative to the estimated true time. This
option allows *chronyd* to run without the capability to adjust or set the
system clock (e.g. in some containers) in order to operate as an NTP server. It
is not recommended to run *chronyd* (with or without *-x*) when another process
is controlling the system clock.
*-v*::
With this option *chronyd* will print version number to the terminal and exit.

View File

@@ -50,7 +50,7 @@ directive can be used for names that resolve to multiple addresses. For good
reliability the client should have at least three servers. The `iburst` option
speeds up the initial synchronisation.
To stabilize the initial synchronisation on the next start, the estimated drift
To stabilise the initial synchronisation on the next start, the estimated drift
of the system clock is saved to a file specified by the `driftfile` directive.
If the system clock can be far from the true time after boot for any reason,
@@ -59,7 +59,7 @@ slewing, which would take a very long time. The `makestep` directive does
that.
In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialized on the
system time is reasonably close to the true time when it's initialised on the
next boot from the RTC, the `rtcsync` directive enables a mode in which the
system time is periodically copied to the RTC. It is supported on Linux and
macOS.
@@ -79,7 +79,7 @@ rtcsync
You need to add an `allow` directive to the _chrony.conf_ file in order to open
the NTP port and allow `chronyd` to reply to client requests. `allow` with no
specified subnet allows all IPv4 and IPv6 addresses.
specified subnet allows access from all IPv4 and IPv6 addresses.
=== I have several computers on a LAN. Should be all clients of an external server?
@@ -97,7 +97,7 @@ _chrony.conf_ file. This configuration will be better because
No. Starting from version 1.25, `chronyd` will keep trying to resolve
the names specified by the `server`, `pool`, and `peer` directives in an
increasing interval until it succeeds. The `online` command can be issued from
`chronyc` to try to resolve them immediately.
`chronyc` to force `chronyd` to try to resolve the names immediately.
=== How can I make `chronyd` more secure?
@@ -161,7 +161,7 @@ The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their
default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 8 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers or if have
for general servers on the Internet. With your own NTP servers, or if you have
permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system
clock.
@@ -171,6 +171,11 @@ network latency and stability of the system clock (which mainly depends on the
temperature sensitivity of the crystal oscillator and the maximum rate of the
temperature change).
Generally, if the `sourcestats` command usually reports a small number of
samples retained for a source (e.g. fewer than 16), a shorter polling interval
should be considered. If the number of samples is usually at the maximum of 64,
a longer polling interval may work better.
An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
@@ -178,15 +183,15 @@ allowed to poll frequently could be
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
----
An example using very short polling intervals for a server located in the same
An example using shorter polling intervals with a server located in the same
LAN could be
----
server ntp.local minpoll 2 maxpoll 4 polltarget 30
----
The maxdelay options are useful to ignore measurements with larger delay (e.g.
due to congestion in the network) and improve the stability of the
The maxdelay options are useful to ignore measurements with an unusally large
delay (e.g. due to congestion in the network) and improve the stability of the
synchronisation. The `maxdelaydevratio` option could be added to the example
with local NTP server
@@ -194,16 +199,78 @@ with local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
If your server supports the interleaved mode, the `xleave` option should be
added to the `server` directive in order to receive server's more accurate
hardware or kernel transmit timestamps. When combined with local hardware
timestamping, a sub-microsecond accuracy may be possible. An example could be
If your server supports the interleaved mode (e.g. it is running `chronyd`),
the `xleave` option should be added to the `server` directive in order to allow
the server to send the client more accurate transmit timestamps (kernel or
preferably hardware). For example:
----
server ntp.local minpoll 2 maxpoll 2 xleave
server ntp.local minpoll 2 maxpoll 4 xleave
----
When combined with local hardware timestamping, good network switches, and even
shorter polling intervals, a sub-microsecond accuracy and stability of a few
tens of nanoseconds may be possible. For example:
----
server ntp.local minpoll 0 maxpoll 0 xleave
hwtimestamp eth0
----
For best stability, the CPU should be running at a constant frequency (i.e.
disabled power saving and performance boosting). Energy-Efficient Ethernet
(EEE) should be disabled in the network. The switches should be configured to
prioritize NTP packets, especially if the network is expected to be heavily
loaded.
If it is acceptable for NTP clients in the network to send requests at an
excessive rate, a sub-second polling interval may be specified. A median filter
can be enabled in order to update the clock at a reduced rate with more stable
measurements. For example:
----
server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
hwtimestamp eth0 minpoll -6
----
=== Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
With the `-Q` option it will print the measured offset without setting the
clock. If you don't want to use a configuration file, NTP servers can be
specified on the command line. For example:
----
# chronyd -q 'pool pool.ntp.org iburst'
----
=== Can `chronyd` be configured to control the clock like `ntpd`?
It is not possible to perfectly emulate `ntpd`, but there are some options that
can configure `chronyd` to behave more like `ntpd`.
In the following example the `minsamples` directive slows down the response to
changes in the frequency and offset of the clock. The `maxslewrate` and
`corrtimeratio` directives reduce the maximum frequency error due to an offset
correction and the `maxdrift` directive reduces the maximum assumed frequency
error of the clock. The `makestep` directive enables a step threshold and the
`maxchange` directive enables a panic threshold. The `maxclockerror` directive
increases the minimum dispersion rate.
----
minsamples 32
maxslewrate 500
corrtimeratio 100
maxdrift 500
makestep 0.128 -1
maxchange 1000 1 1
maxclockerror 15
----
Note that increasing `minsamples` may cause the offsets in the `tracking` and
`sourcestats` reports/logs to be significantly smaller than the actual offsets
and be unsuitable for monitoring.
=== What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the
@@ -242,8 +309,17 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
=== Are NTP servers specified with the `offline` option?
Check that you're using ``chronyc``'s `online` and `offline` commands
appropriately. Again, check in _measurements.log_ to see if you're getting any
data back from the server.
appropriately. The `activity` command prints the number of sources that are
currently online and offline. For example:
----
200 OK
3 sources online
0 sources offline
0 sources doing burst (return to online)
0 sources doing burst (return to offline)
0 sources with unknown address
----
=== Is `chronyd` allowed to step the system clock?
@@ -271,6 +347,49 @@ to
makestep 1 -1
----
=== Using a Windows NTP server?
A common issue with Windows NTP servers is that they report a very large root
dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
server for being too inaccurate. The `sources` command may show a valid
measurement, but the server is not selected for synchronisation. You can check
the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
The `maxdistance` value needs to be increased in _chrony.conf_ to enable
synchronisation to such a server. For example:
----
maxdistance 16.0
----
=== Using a PPS reference clock?
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
determine which second of UTC corresponds to each pulse. If it is another
reference clock specified with the `lock` option in the `refclock` directive,
the offset between the two reference clocks must be smaller than 0.2 seconds in
order for the PPS reference clock to work. With NMEA reference clocks it is
common to have a larger offset. It needs to be corrected with the `offset`
option.
One approach to find out a good value of the `offset` option is to configure
the reference clocks with the `noselect` option and compare them to an NTP
server. For example, if the `sourcestats` command showed
----
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
foo.example.net 7 3 200 -2.991 16.141 -107us 492us
----
the offset of the NMEA source would need to be increased by about 0.504
seconds. It does not have to be very accurate. As long as the offset of the
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
able to determine the seconds corresponding to the pulses and allow the samples
to be used for synchronisation.
== Issues with `chronyc`
=== I keep getting the error `506 Cannot talk to daemon`
@@ -326,14 +445,14 @@ Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
=== What is the real-time clock (RTC)?
This is the clock which keeps the time even when your computer is turned off.
It is used to initialize the system clock on boot. It normally doesn't drift
It is used to initialise the system clock on boot. It normally doesn't drift
more than few seconds per day.
There are two approaches how `chronyd` can work with it. One is to use the
`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
the RTC from the system clock every 11 minutes. `chronyd` itself won't touch
the RTC. If the computer is not turned off for a long time, the RTC should
still be close to the true time when the system clock will be initialized from
still be close to the true time when the system clock will be initialised from
it on the next boot.
The other option is to use the `rtcfile` directive, which tells `chronyd` to
@@ -372,16 +491,31 @@ things
Some other program running on the system may be using the device.
=== What if my computer does not have an RTC or backup battery?
In this case you can still use the `-s` option to set the system clock to the
last modification time of the drift file, which should correspond to the system
time when `chronyd` was previously stopped. The initial system time will be
increasing across reboots and applications started after `chronyd` will not
observe backward steps.
== NTP-specific issues
=== Can `chronyd` be driven from broadcast NTP servers?
=== Can `chronyd` be driven from broadcast/multicast NTP servers?
No, the broadcast client mode is not supported and there is currently no plan
to implement it. The broadcast and multicast modes are inherently less
accurate and less secure (even with authentication) than the ordinary
server/client mode and they are not as useful as they used to be. Even with
very modest hardware a single NTP server can serve time to hundreds of
thousands of clients using the ordinary mode.
No, the broadcast/multicast client mode is not supported and there is currently
no plan to implement it. While the mode may be useful to simplify configuration
of clients in large networks, it is inherently less accurate and less secure
(even with authentication) than the ordinary client/server mode.
When configuring a large number of clients in a network, it is recommended to
use the `pool` directive with a DNS name which resolves to addresses of
multiple NTP servers. The clients will automatically replace the servers when
they become unreachable, or otherwise unsuitable for synchronisation, with new
servers from the pool.
Even with very modest hardware, an NTP server can serve time to hundreds of
thousands of clients using the ordinary client/server mode.
=== Can `chronyd` transmit broadcast NTP packets?
@@ -396,15 +530,15 @@ option for all time sources in the _chrony.conf_ file.
=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
`chronyd` will keep trying to access the server(s) that it thinks are online.
When the network is connected again, it will take some time (on average half of
the maximum polling interval) before new measurements are made and the clock is
corrected. If the servers were set to offline and the `online` command was
issued when the network was connected, `chronyd` would make new measurements
immediately.
`chronyd` will keep trying to access the sources that it thinks are online, and
it will take longer before new measurements are actually made and the clock is
corrected when the network is connected again. If the sources were set to
offline, `chronyd` would make new measurements immediately after issuing the
`online` command.
The `auto_offline` option to the `server` entry in the _chrony.conf_ file may
be useful to switch the servers to the offline state automatically.
Unless the network connection lasts only few minutes (less than the maximum
polling interval), the delay is usually not a problem, and it may be acceptable
to keep all sources online all the time.
== Operating systems

View File

@@ -22,6 +22,19 @@ The software is distributed as source code which has to be compiled. The source
code is supplied in the form of a gzipped tar file, which unpacks to a
subdirectory identifying the name and version of the program.
The following programs and libraries with their development files are needed to
build `chrony`:
* C compiler (gcc or clang recommended)
* GNU Make
* Nettle, NSS, or LibTomCrypt (optional)
* Editline (optional)
* libcap (Linux only, optional)
* libseccomp (Linux only, optional)
* timepps.h header (optional)
* Asciidoctor (for HTML documentation)
* Bash (for testing)
After unpacking the source code, change directory into it, and type
----
@@ -29,8 +42,8 @@ After unpacking the source code, change directory into it, and type
----
This is a shell script that automatically determines the system type. There is
a single optional parameter, `--prefix` which indicates the directory tree
where the software should be installed. For example,
an optional parameter `--prefix`, which indicates the directory tree where the
software should be installed. For example,
----
./configure --prefix=/opt/free
@@ -40,11 +53,11 @@ will install the `chronyd` daemon into `/opt/free/sbin` and the `chronyc`
control program into `/opt/free/bin`. The default value for the prefix is
`/usr/local`.
The configure script assumes you want to use gcc as your compiler. If you want
to use a different compiler, you can configure this way:
The `configure` script assumes you want to use `gcc` as your compiler. If you
want to use a different compiler, you can configure this way:
----
CC=cc CFLAGS=-O ./configure --prefix=/opt/free
CC=cc ./configure --prefix=/opt/free
----
for Bourne-family shells, or
@@ -63,11 +76,26 @@ shown. Otherwise, `Makefile` will be generated.
On Linux, if development files for the libcap library are available, `chronyd`
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.
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
http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
`chronyd` will be built with support for other cryptographic hash functions
than MD5, which can be used for NTP authentication with a symmetric key. If you
don't want to enable the support, specify the `--disable-sechash` flag to
`configure`.
If development files for the editline or readline library are available,
`chronyc` will be built with line editing support. If you don't want this,
specify the `--disable-readline` flag to configure.
specify the `--disable-readline` flag to `configure`.
If a `timepps.h` header is available (e.g. from the
http://linuxpps.org[LinuxPPS project]), `chronyd` will be built with PPS API
@@ -75,6 +103,9 @@ reference clock driver. If the header is installed in a location that isn't
normally searched by the compiler, you can add it to the searched locations by
setting the `CPPFLAGS` variable to `-I/path/to/timepps`.
The `--help` option can be specified to `configure` to print all options
supported by the script.
Now type
----
@@ -122,6 +153,16 @@ unprivileged user for `chronyd` and specify it with the `-u` command-line
option or the `user` directive in the configuration file, or set the default
user with the `--with-user` configure option before building.
== Support for system call filtering
`chronyd` can be built with support for the Linux secure computing (seccomp)
facility. This requires development files for the
https://github.com/seccomp/libseccomp[libseccomp] library and the
`--enable-scfilter` option specified to `configure`. The `-F` option of
`chronyd` will enable a system call filter, which should significantly reduce
the kernel attack surface and possibly prevent kernel exploits from `chronyd`
if it is compromised.
== Support for line editing libraries
`chronyc` can be built with support for line editing, this allows you to use
@@ -132,12 +173,12 @@ Please note that readline since version 6.0 is licensed under GPLv3+ which is
incompatible with chrony's license GPLv2. You should use editline instead if
you don't want to use older readline versions.
The configure script will automatically enable the line editing support if one
of the supported libraries is available. If they are both available, the
The `configure` script will automatically enable the line editing support if
one of the supported libraries is available. If they are both available, the
editline library will be used.
If you don't want to use it (in which case chronyc will use a minimal command
line interface), invoke configure like this:
If you don't want to use it (in which case `chronyc` will use a minimal command
line interface), invoke `configure` like this:
----
./configure --disable-readline other-options...
@@ -161,12 +202,12 @@ normally searched by the compiler and linker, you need to use extra options:
== Extra options for package builders
The configure and make procedures have some extra options that may be useful if
you are building a distribution package for chrony.
The `configure` and `make` procedures have some extra options that may be
useful if you are building a distribution package for `chrony`.
The `--mandir=DIR` option to configure specifies an install directory for the
man pages. This overrides the `man` subdirectory of the argument to the
--prefix option.
The `--mandir=DIR` option to `configure` specifies an installation directory
for the man pages. This overrides the `man` subdirectory of the argument to the
`--prefix` option.
----
./configure --prefix=/usr --mandir=/usr/share/man
@@ -174,8 +215,8 @@ man pages. This overrides the `man` subdirectory of the argument to the
to set both options together.
The final option is the `DESTDIR` option to the make command. For example, you
could use the commands
The final option is the `DESTDIR` option to the `make` command. For example,
you could use the commands
----
./configure --prefix=/usr --mandir=/usr/share/man

View File

@@ -1,5 +1,6 @@
[Unit]
Description=Wait for chrony to synchronize system clock
Documentation=man:chronyc(1)
After=chronyd.service
Requires=chronyd.service
Before=time-sync.target

View File

@@ -4,8 +4,8 @@ pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 1 second.
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).

View File

@@ -5,22 +5,32 @@ pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 1 second.
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync
# Allow NTP client access from local network.
#allow 192.168/16
# Enable hardware timestamping on all interfaces that support it.
#hwtimestamp *
# Serve time even if not synchronized to any NTP server.
# Increase the minimum number of selectable sources required to adjust
# the system clock.
#minsources 2
# Allow NTP client access from local network.
#allow 192.168.0.0/16
# Serve time even if not synchronized to a time source.
#local stratum 10
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC
# Specify directory for log files.
logdir /var/log/chrony

View File

@@ -33,42 +33,30 @@
! pool pool.ntp.org iburst
# However, for dial-up use you probably want these instead. The word
# 'offline' means that the server is not visible at boot time. Use
# chronyc's 'online' command to tell chronyd that these servers have
# become visible after you go on-line.
! server foo.example.net offline
! server bar.example.net offline
! server baz.example.net offline
! pool pool.ntp.org offline
# You may want to specify NTP 'peers' instead. If you run a network
# with a lot of computers and want several computers running chrony to
# have the 'front-line' interface to the public NTP servers, you can
# 'peer' these machines together to increase robustness.
! peer foo.example.net
# There are other options to the 'server' and 'peer' directives that you
# might want to use. For example, you can ignore measurements whose
# round-trip-time is too large (indicating that the measurement is
# probably useless, because you don't know which way the measurement
# message got held up.) Consult the full documentation for details.
#######################################################################
### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK
#
# To avoid changes being made to your computer's gain/loss compensation
# when the measurement history is too erratic, you might want to enable
# one of the following lines. The first seems good for dial-up (or
# other high-latency connections like slow leased lines), the second
# seems OK for a LAN environment.
# one of the following lines. The first seems good with servers on the
# Internet, the second seems OK for a LAN environment.
! maxupdateskew 100
! maxupdateskew 5
# If you want to increase the minimum number of selectable sources
# required to update the system clock in order to make the
# synchronisation more reliable, uncomment (and edit) the following
# line.
! minsources 2
# If your computer has a good stable clock (e.g. it is not a virtual
# machine), you might also want to reduce the maximum assumed drift
# (frequency error) of the clock (the value is specified in ppm).
! maxdrift 100
#######################################################################
### FILENAMES ETC
# Chrony likes to keep information about your computer's clock in files.
@@ -107,7 +95,13 @@ driftfile /var/lib/chrony/drift
# still running and bail out. If you want to change the path to the PID
# file, uncomment this line and edit it. The default path is shown.
! pidfile /var/run/chronyd.pid
! 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.
! leapsectz right/UTC
#######################################################################
### INITIAL CLOCK CORRECTION
@@ -181,13 +175,12 @@ driftfile /var/lib/chrony/drift
# machine accesses it. The information can be accessed by the 'clients'
# command of chronyc. You can disable this facility by uncommenting the
# following line. This will save a bit of memory if you have many
# clients.
# clients and it will also disable support for the interleaved mode.
! noclientlog
# The clientlog size is limited to 512KB by default. If you have many
# clients, especially in many different subnets, you might want to
# increase the limit.
# clients, you might want to increase the limit.
! clientloglimit 4194304
@@ -196,7 +189,7 @@ driftfile /var/lib/chrony/drift
# clients that are sending requests too frequently, uncomment and edit
# the following line.
! limitrate interval 3 burst 8
! ratelimit interval 3 burst 8
#######################################################################
### REPORTING BIG CLOCK CHANGES
@@ -243,7 +236,17 @@ driftfile /var/lib/chrony/drift
# Rate limiting can be enabled also for command packets. (Note,
# commands from localhost are never limited.)
! cmdratelimit interval 1 burst 16
! cmdratelimit interval -4 burst 16
#######################################################################
### HARDWARE TIMESTAMPING
# On Linux, if the network interface controller and its driver support
# hardware timestamping, it can significantly improve the accuracy of
# synchronisation. It can be enabled on specified interfaces only, or it
# can be enabled on all interfaces that support it.
! hwtimestamp eth0
! hwtimestamp *
#######################################################################
### REAL TIME CLOCK
@@ -274,6 +277,12 @@ driftfile /var/lib/chrony/drift
! rtcdevice /dev/misc/rtc
# Alternatively, if not using the -s option, this directive can be used
# to enable a mode in which the RTC is periodically set to the system
# time, with no tracking of its drift.
! rtcsync
#######################################################################
### REAL TIME SCHEDULER
# This directive tells chronyd to use the real-time FIFO scheduler with the

View File

@@ -1,6 +1,7 @@
# This is an example chrony keys file. It is used for NTP authentication with
# symmetric keys. It should be readable only by root or the user to which
# chronyd is configured to switch to after start.
# This is an example chrony keys file. It enables authentication of NTP
# packets with symmetric keys when its location is specified by the keyfile
# directive in chrony.conf(5). It should be readable only by root and the
# user under which chronyd is running.
#
# Don't use the example keys! It's recommended to generate random keys using
# the chronyc keygen command.

View File

@@ -1,17 +1,15 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
# online/offline when a default route is configured/removed on the system.
# This is a NetworkManager dispatcher / networkd-dispatcher script for
# chronyd to set its NTP sources online or offline when a network interface
# is configured or removed
export LC_ALL=C
if [ "$2" = "up" ]; then
/sbin/ip route list dev "$1" | grep -q '^default' &&
/usr/bin/chronyc online > /dev/null 2>&1
fi
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
if [ "$2" = "down" ]; then
/sbin/ip route list | grep -q '^default' ||
/usr/bin/chronyc offline > /dev/null 2>&1
fi
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
chronyc onoffline > /dev/null 2>&1
exit 0

View File

@@ -1,46 +0,0 @@
%global chrony_version @@VERSION@@
%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
%endif
Summary: An NTP client/server
Name: chrony
Version: %(echo %{chrony_version} | sed 's/-.*//')
Release: %{!?prerelease:1}%{?prerelease:0.1.%{prerelease}}
Source: chrony-%{version}%{?prerelease:-%{prerelease}}.tar.gz
License: GPLv2
Group: Applications/Utilities
BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n)
%description
chrony is a client and server for the Network Time Protocol (NTP).
This program keeps your computer's clock accurate. It was specially
designed to support systems with intermittent Internet connections,
but it also works well in permanently connected environments. It can
also use hardware reference clocks, the system real-time clock, or
manual input as time references.
%prep
%setup -q -n %{name}-%{version}%{?prerelease:-%{prerelease}}
%build
./configure \
--prefix=%{_prefix} \
--bindir=%{_bindir} \
--sbindir=%{_sbindir} \
--mandir=%{_mandir}
make
%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
%files
%{_sbindir}/chronyd
%{_bindir}/chronyc
%{_mandir}/man1/chronyc.1.gz
%{_mandir}/man5/chrony.conf.5.gz
%{_mandir}/man8/chronyd.8.gz
%doc README FAQ NEWS COPYING
%doc examples/chrony.conf.example*
%doc examples/chrony.keys.example

View File

@@ -1,13 +1,18 @@
[Unit]
Description=NTP client/server
Documentation=man:chronyd(8) man:chrony.conf(5)
After=ntpdate.service sntp.service ntpd.service
Conflicts=ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
PIDFile=/var/run/chronyd.pid
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
[Install]
WantedBy=multi-user.target

View File

@@ -29,6 +29,7 @@
#include "sysincl.h"
#include "hash.h"
#include "memory.h"
#include "util.h"
#include "md5.c"
@@ -49,18 +50,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
if (out_len < 16)
return 0;
MD5Init(&ctx);
MD5Update(&ctx, in1, in1_len);
if (in2)
MD5Update(&ctx, in2, in2_len);
MD5Final(&ctx);
memcpy(out, ctx.digest, 16);
out_len = MIN(out_len, 16);
return 16;
memcpy(out, ctx.digest, out_len);
return out_len;
}
void

120
hash_nettle.c Normal file
View File

@@ -0,0 +1,120 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2018
*
* 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.
*
**********************************************************************
=======================================================================
Routines implementing crypto hashing using the nettle library.
*/
#include "config.h"
#include "sysincl.h"
#include <nettle/nettle-meta.h>
#include "hash.h"
#include "memory.h"
struct hash {
const char *name;
const char *int_name;
const struct nettle_hash *nettle_hash;
void *context;
};
static struct hash hashes[] = {
{ "MD5", "md5", NULL, NULL },
{ "RMD160", "ripemd160", NULL, NULL },
{ "SHA1", "sha1", NULL, NULL },
{ "SHA256", "sha256", NULL, NULL },
{ "SHA384", "sha384", NULL, NULL },
{ "SHA512", "sha512", NULL, NULL },
{ "SHA3-224", "sha3_224", NULL, NULL },
{ "SHA3-256", "sha3_256", NULL, NULL },
{ "SHA3-384", "sha3_384", NULL, NULL },
{ "SHA3-512", "sha3_512", NULL, NULL },
{ NULL, NULL, NULL, NULL }
};
int
HSH_GetHashId(const char *name)
{
int id, nid;
for (id = 0; hashes[id].name; id++) {
if (!strcmp(name, hashes[id].name))
break;
}
if (!hashes[id].name)
return -1;
if (hashes[id].context)
return id;
for (nid = 0; nettle_hashes[nid]; nid++) {
if (!strcmp(hashes[id].int_name, nettle_hashes[nid]->name))
break;
}
if (!nettle_hashes[nid] || !nettle_hashes[nid]->context_size || !nettle_hashes[nid]->init)
return -1;
hashes[id].nettle_hash = nettle_hashes[nid];
hashes[id].context = Malloc(hashes[id].nettle_hash->context_size);
return id;
}
unsigned int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
const struct nettle_hash *hash;
void *context;
hash = hashes[id].nettle_hash;
context = hashes[id].context;
if (out_len > hash->digest_size)
out_len = hash->digest_size;
hash->init(context);
hash->update(context, in1_len, in1);
if (in2)
hash->update(context, in2_len, in2);
hash->digest(context, out_len, out);
return out_len;
}
void
HSH_Finalise(void)
{
int i;
for (i = 0; hashes[i].name; i++) {
if (hashes[i].context)
Free(hashes[i].context);
}
}

View File

@@ -32,6 +32,7 @@
#include <nsslowhash.h>
#include "hash.h"
#include "util.h"
static NSSLOWInitContext *ictx;
@@ -78,13 +79,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
unsigned int ret;
unsigned char buf[MAX_HASH_LENGTH];
unsigned int ret = 0;
NSSLOWHASH_Begin(hashes[id].context);
NSSLOWHASH_Update(hashes[id].context, in1, in1_len);
if (in2)
NSSLOWHASH_Update(hashes[id].context, in2, in2_len);
NSSLOWHASH_End(hashes[id].context, out, &ret, out_len);
NSSLOWHASH_End(hashes[id].context, buf, &ret, sizeof (buf));
ret = MIN(ret, out_len);
memcpy(out, buf, ret);
return ret;
}

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2012
* Copyright (C) Miroslav Lichvar 2012, 2018
*
* 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
@@ -29,6 +29,7 @@
#include "config.h"
#include "hash.h"
#include "util.h"
struct hash {
const char *name;
@@ -62,6 +63,12 @@ static const struct hash hashes[] = {
#ifdef LTC_SHA512
{ "SHA512", "sha512", &sha512_desc },
#endif
#ifdef LTC_SHA3
{ "SHA3-224", "sha3-224", &sha3_224_desc },
{ "SHA3-256", "sha3-256", &sha3_256_desc },
{ "SHA3-384", "sha3-384", &sha3_384_desc },
{ "SHA3-512", "sha3-512", &sha3_512_desc },
#endif
#ifdef LTC_TIGER
{ "TIGER", "tiger", &tiger_desc },
#endif
@@ -99,19 +106,24 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
unsigned long len;
int r;
len = out_len;
len = sizeof (buf);
if (in2)
r = hash_memory_multi(id, out, &len,
in1, (unsigned long)in1_len, in2, (unsigned long)in2_len, NULL, 0);
r = hash_memory_multi(id, buf, &len,
in1, (unsigned long)in1_len,
in2, (unsigned long)in2_len, NULL, 0);
else
r = hash_memory(id, in1, in1_len, out, &len);
r = hash_memory(id, in1, in1_len, buf, &len);
if (r != CRYPT_OK)
return 0;
len = MIN(len, out_len);
memcpy(out, buf, len);
return len;
}

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
* Copyright (C) Miroslav Lichvar 2016-2018
*
* 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
@@ -36,11 +36,12 @@
#include "regress.h"
#include "util.h"
/* Maximum number of samples per clock */
#define MAX_SAMPLES 16
/* Minimum and maximum number of samples per clock */
#define MIN_SAMPLES 2
#define MAX_SAMPLES 64
/* Minimum interval between samples (in seconds) */
#define MIN_SAMPLE_SEPARATION 1.0
/* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0)
struct HCL_Instance_Record {
/* HW and local reference timestamp */
@@ -49,15 +50,20 @@ struct HCL_Instance_Record {
/* Samples stored as intervals (uncorrected for frequency error)
relative to local_ref and hw_ref */
double x_data[MAX_SAMPLES];
double y_data[MAX_SAMPLES];
double *x_data;
double *y_data;
/* Number of samples */
/* Minimum, maximum and current number of samples */
int min_samples;
int max_samples;
int n_samples;
/* Maximum error of the last sample */
double last_err;
/* Minimum interval between samples */
double min_separation;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
@@ -86,15 +92,24 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
HCL_Instance
HCL_CreateInstance(void)
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
{
HCL_Instance clock;
min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
max_samples = MAX(min_samples, max_samples);
clock = MallocNew(struct HCL_Instance_Record);
clock->x_data[MAX_SAMPLES - 1] = 0.0;
clock->y_data[MAX_SAMPLES - 1] = 0.0;
clock->x_data = MallocArray(double, max_samples);
clock->y_data = MallocArray(double, max_samples);
clock->x_data[max_samples - 1] = 0.0;
clock->y_data[max_samples - 1] = 0.0;
clock->min_samples = min_samples;
clock->max_samples = max_samples;
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
LCL_AddParameterChangeHandler(handle_slew, clock);
@@ -106,6 +121,8 @@ HCL_CreateInstance(void)
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
Free(clock->y_data);
Free(clock->x_data);
Free(clock);
}
@@ -115,7 +132,7 @@ int
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
{
if (!clock->n_samples ||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= MIN_SAMPLE_SEPARATION)
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= clock->min_separation)
return 1;
return 0;
@@ -134,18 +151,18 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
/* Shift old samples */
if (clock->n_samples) {
if (clock->n_samples >= MAX_SAMPLES)
if (clock->n_samples >= clock->max_samples)
clock->n_samples--;
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
if (hw_delta <= 0.0 || local_delta < MIN_SAMPLE_SEPARATION / 2.0) {
if (hw_delta <= 0.0 || local_delta < clock->min_separation / 2.0) {
clock->n_samples = 0;
DEBUG_LOG(LOGF_HwClocks, "HW clock reset interval=%f", local_delta);
DEBUG_LOG("HW clock reset interval=%f", local_delta);
}
for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
for (i = clock->max_samples - clock->n_samples; i < clock->max_samples; i++) {
clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
clock->x_data[i - 1] = clock->x_data[i] - local_delta;
}
@@ -158,31 +175,33 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
/* Get new coefficients */
clock->valid_coefs =
RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
clock->y_data + MAX_SAMPLES - clock->n_samples,
clock->n_samples, 1.0e-9, &clock->offset, &raw_freq,
RGR_FindBestRobustRegression(clock->x_data + clock->max_samples - clock->n_samples,
clock->y_data + clock->max_samples - clock->n_samples,
clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
&n_runs, &best_start);
if (!clock->valid_coefs) {
DEBUG_LOG(LOGF_HwClocks, "HW clock needs more samples");
DEBUG_LOG("HW clock needs more samples");
return;
}
clock->frequency = raw_freq / local_freq;
/* Drop unneeded samples */
clock->n_samples -= best_start;
if (clock->n_samples > clock->min_samples)
clock->n_samples -= MIN(best_start, clock->n_samples - clock->min_samples);
/* If the fit doesn't cross the error interval of the last sample, throw away
all previous samples and keep only the frequency estimate */
if (fabs(clock->offset) > err) {
DEBUG_LOG(LOGF_HwClocks, "HW clock reset offset=%e", clock->offset);
clock->offset = 0.0;
clock->n_samples = 1;
/* If the fit doesn't cross the error interval of the last sample,
or the frequency is not sane, drop all samples and start again */
if (fabs(clock->offset) > err ||
fabs(clock->frequency - 1.0) > MAX_FREQ_OFFSET) {
DEBUG_LOG("HW clock reset");
clock->n_samples = 0;
clock->valid_coefs = 0;
}
DEBUG_LOG(LOGF_HwClocks, "HW clock samples=%d offset=%e freq=%.9e raw_freq=%.9e err=%e ref_diff=%e",
clock->n_samples, clock->offset, clock->frequency, raw_freq, err,
DEBUG_LOG("HW clock samples=%d offset=%e freq=%e raw_freq=%e err=%e ref_diff=%e",
clock->n_samples, clock->offset, clock->frequency - 1.0, raw_freq - 1.0, err,
UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
}
@@ -197,7 +216,7 @@ HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
return 0;
elapsed = UTI_DiffTimespecsToDouble(raw, &clock->hw_ref);
offset = clock->offset + elapsed / clock->frequency;
offset = elapsed / clock->frequency - clock->offset;
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
/* Fow now, just return the error of the last sample */

View File

@@ -29,7 +29,8 @@
typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(void);
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
double min_separation);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);

14
keys.c
View File

@@ -107,6 +107,8 @@ determine_hash_delay(uint32_t key_id)
double diff, min_diff;
int i, nsecs;
memset(&pkt, 0, sizeof (pkt));
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH,
@@ -122,7 +124,7 @@ determine_hash_delay(uint32_t key_id)
/* Add on a bit extra to allow for copying, conversions etc */
nsecs = 1.0625e9 * min_diff;
DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
return nsecs;
}
@@ -200,7 +202,7 @@ KEY_Reload(void)
in = fopen(key_file, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_Keys, "Could not open keyfile %s", key_file);
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
return;
}
@@ -212,19 +214,19 @@ KEY_Reload(void)
continue;
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
LOG(LOGS_WARN, LOGF_Keys, "Could not parse key at line %d in file %s", line_number, key_file);
LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
continue;
}
key.hash_id = HSH_GetHashId(hashname);
if (key.hash_id < 0) {
LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %"PRIu32, key_id);
LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id);
continue;
}
key.len = decode_password(keyval);
if (!key.len) {
LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id);
LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id);
continue;
}
@@ -244,7 +246,7 @@ KEY_Reload(void)
/* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id)
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
}
/* Erase any passwords from stack */

22
local.c
View File

@@ -146,7 +146,7 @@ calculate_sys_precision(void)
assert(precision_log >= -30);
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
}
/* ================================================== */
@@ -358,12 +358,12 @@ LCL_ReadRawTime(struct timespec *ts)
{
#if HAVE_CLOCK_GETTIME
if (clock_gettime(CLOCK_REALTIME, ts) < 0)
LOG_FATAL(LOGF_Local, "clock_gettime() failed : %s", strerror(errno));
LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
#else
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
LOG_FATAL(LOGF_Local, "gettimeofday() failed : %s", strerror(errno));
LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
UTI_TimevalToTimespec(&tv, ts);
#endif
@@ -426,7 +426,7 @@ clamp_freq(double freq)
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
return freq;
LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq);
LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
}
@@ -440,7 +440,7 @@ check_offset(struct timespec *now, double offset)
if (UTI_IsTimeOffsetSane(now, -offset))
return 1;
LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset);
LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
return 0;
}
@@ -546,7 +546,7 @@ LCL_ApplyStepOffset(double offset)
return 0;
if (!(*drv_apply_step_offset)(offset)) {
LOG(LOGS_ERR, LOGF_Local, "Could not step clock");
LOG(LOGS_ERR, "Could not step system clock");
return 0;
}
@@ -613,7 +613,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
current_freq_ppm = clamp_freq(current_freq_ppm);
DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
old_freq_ppm, current_freq_ppm, doffset);
/* Call the system-specific driver for setting the frequency */
@@ -660,7 +660,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
current_freq_ppm = (*drv_read_freq)();
DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
}
/* ================================================== */
@@ -684,7 +684,7 @@ LCL_MakeStep(void)
if (!LCL_ApplyStepOffset(-correction))
return 0;
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
return 1;
}
@@ -700,10 +700,10 @@ LCL_CanSystemLeap(void)
/* ================================================== */
void
LCL_SetSystemLeap(int leap)
LCL_SetSystemLeap(int leap, int tai_offset)
{
if (drv_set_leap) {
(drv_set_leap)(leap);
(drv_set_leap)(leap, tai_offset);
}
}

View File

@@ -201,10 +201,11 @@ extern int LCL_MakeStep(void);
does something */
extern int LCL_CanSystemLeap(void);
/* Routine to set the system clock to correct itself for a leap second if
supported. Leap second will be inserted at the end of the day if the
argument is positive, deleted if negative, and zero resets the setting. */
extern void LCL_SetSystemLeap(int leap);
/* Routine to set the system clock to correct itself for a leap second and also
set its TAI-UTC offset. If supported, leap second will be inserted at the
end of the day if the argument is positive, deleted if negative, and zero
resets the setting. */
extern void LCL_SetSystemLeap(int leap, int tai_offset);
/* Routine to set a frequency correction (in ppm) that should be applied
to local clock to compensate for temperature changes. A positive

View File

@@ -54,8 +54,8 @@ typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
raw time to get the corrected time */
typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err);
/* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap);
/* System driver to schedule leap seconds and set TAI-UTC offset */
typedef void (*lcl_SetLeapDriver)(int leap, int tai_offset);
/* System driver to set the synchronisation status */
typedef void (*lcl_SetSyncStatusDriver)(int synchronised, double est_error, double max_error);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014
* Copyright (C) Miroslav Lichvar 2011-2014, 2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -40,6 +40,7 @@ int log_debug_enabled = 0;
/* Flag indicating we have initialised */
static int initialised = 0;
static FILE *file_log;
static int system_log = 0;
static int parent_fd = 0;
@@ -69,6 +70,7 @@ void
LOG_Initialise(void)
{
initialised = 1;
file_log = stderr;
}
/* ================================================== */
@@ -77,9 +79,11 @@ LOG_Initialise(void)
void
LOG_Finalise(void)
{
if (system_log) {
if (system_log)
closelog();
}
if (file_log)
fclose(file_log);
LOG_CycleLogFiles();
@@ -112,8 +116,8 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
assert(0);
}
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else {
fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message);
} else if (file_log) {
fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
}
@@ -121,25 +125,26 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
void LOG_Message(LOG_Severity severity,
#if DEBUG > 0
LOG_Facility facility, int line_number,
const char *filename, const char *function_name,
int line_number, const char *filename, const char *function_name,
#endif
const char *format, ...)
{
char buf[2048];
va_list other_args;
time_t t;
struct tm stm;
struct tm *tm;
if (!system_log) {
if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
fprintf(stderr, "%s ", buf);
tm = gmtime(&t);
if (tm) {
strftime(buf, sizeof (buf), "%Y-%m-%dT%H:%M:%SZ", tm);
fprintf(file_log, "%s ", buf);
}
#if DEBUG > 0
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name);
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
#endif
}
@@ -157,23 +162,47 @@ void LOG_Message(LOG_Severity severity,
case LOGS_FATAL:
log_message(1, severity, buf);
/* With syslog, send the message also to the grandparent
process or write it to stderr if not detached */
if (system_log) {
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
; /* Not much we can do here */
} else if (parent_fd == 0) {
system_log = 0;
log_message(1, severity, buf);
}
/* 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)
; /* Not much we can do here */
} else if (system_log && parent_fd == 0) {
system_log = 0;
log_message(1, severity, buf);
}
exit(1);
break;
default:
assert(0);
}
}
/* ================================================== */
void
LOG_OpenFileLog(const char *log_file)
{
FILE *f;
if (log_file) {
f = fopen(log_file, "a");
if (!f)
LOG_FATAL("Could not open log file %s", log_file);
} else {
f = stderr;
}
/* Enable line buffering */
setvbuf(f, NULL, _IOLBF, BUFSIZ);
if (file_log && file_log != stderr)
fclose(file_log);
file_log = f;
}
/* ================================================== */
void
@@ -199,6 +228,8 @@ void
LOG_SetParentFd(int fd)
{
parent_fd = fd;
if (file_log == stderr)
file_log = NULL;
}
/* ================================================== */
@@ -241,7 +272,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
char filename[512], *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') {
LOG(LOGS_WARN, LOGF_Logging, "logdir not specified");
LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL;
return;
}
@@ -249,7 +280,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
if (snprintf(filename, sizeof(filename), "%s/%s.log",
logdir, logfiles[id].name) >= sizeof (filename) ||
!(logfiles[id].file = fopen(filename, "a"))) {
LOG(LOGS_WARN, LOGF_Logging, "Could not open log file %s", filename);
LOG(LOGS_WARN, "Could not open log file %s", filename);
logfiles[id].name = NULL;
return;
}

View File

@@ -46,26 +46,26 @@ extern int log_debug_enabled;
#endif
#if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#else
#define LOG_MESSAGE(severity, facility, ...) \
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __VA_ARGS__)
#endif
#define DEBUG_LOG(facility, ...) \
#define DEBUG_LOG(...) \
do { \
if (DEBUG && log_debug_enabled) \
LOG_MESSAGE(LOGS_DEBUG, facility, __VA_ARGS__); \
LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
} while (0)
#define LOG_FATAL(facility, ...) \
#define LOG_FATAL(...) \
do { \
LOG_MESSAGE(LOGS_FATAL, facility, __VA_ARGS__); \
LOG_MESSAGE(LOGS_FATAL, __VA_ARGS__); \
exit(1); \
} while (0)
#define LOG(severity, facility, ...) LOG_MESSAGE(severity, facility, __VA_ARGS__)
#define LOG(severity, ...) LOG_MESSAGE(severity, __VA_ARGS__)
/* Definition of severity */
typedef enum {
@@ -76,50 +76,6 @@ typedef enum {
LOGS_DEBUG
} LOG_Severity;
/* Definition of facility. Each message is tagged with who generated
it, so that the user can customise what level of reporting he gets
for each area of the software */
typedef enum {
LOGF_Reference,
LOGF_NtpIO,
LOGF_NtpIOLinux,
LOGF_NtpCore,
LOGF_NtpSignd,
LOGF_NtpSources,
LOGF_Scheduler,
LOGF_SourceStats,
LOGF_Sources,
LOGF_Local,
LOGF_Util,
LOGF_Main,
LOGF_Memory,
LOGF_Client,
LOGF_ClientLog,
LOGF_Configure,
LOGF_CmdMon,
LOGF_Acquire,
LOGF_Manual,
LOGF_Keys,
LOGF_Logging,
LOGF_Nameserv,
LOGF_PrivOps,
LOGF_Rtc,
LOGF_Regress,
LOGF_Sys,
LOGF_SysGeneric,
LOGF_SysLinux,
LOGF_SysMacOSX,
LOGF_SysNetBSD,
LOGF_SysSolaris,
LOGF_SysTimex,
LOGF_SysWinnt,
LOGF_TempComp,
LOGF_RtcLinux,
LOGF_Refclock,
LOGF_HwClocks,
LOGF_Smooth,
} LOG_Facility;
/* Init function */
extern void LOG_Initialise(void);
@@ -128,9 +84,8 @@ extern void LOG_Finalise(void);
/* Line logging function */
#if DEBUG > 0
FORMAT_ATTRIBUTE_PRINTF(6, 7)
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename,
FORMAT_ATTRIBUTE_PRINTF(5, 6)
extern void LOG_Message(LOG_Severity severity, int line_number, const char *filename,
const char *function_name, const char *format, ...);
#else
FORMAT_ATTRIBUTE_PRINTF(2, 3)
@@ -144,10 +99,13 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
*/
extern void LOG_SetDebugLevel(int level);
/* Log messages to a file instead of stderr, or stderr again if NULL */
extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void);
/* Send fatal message also to the foreground process */
/* 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 */

324
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2015
* Copyright (C) Miroslav Lichvar 2012-2018
*
* 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
@@ -86,6 +86,10 @@ static void
delete_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
if (!pidfile[0])
return;
/* Don't care if this fails, there's not a lot we can do */
unlink(pidfile);
}
@@ -97,7 +101,7 @@ MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
if (CNF_GetDumpOnExit()) {
if (CNF_GetDumpDir()[0] != '\0') {
SRC_DumpSources();
}
@@ -127,9 +131,8 @@ MAI_CleanupAndExit(void)
delete_pidfile();
CNF_Finalise();
LOG_Finalise();
HSH_Finalise();
LOG_Finalise();
exit(exit_status);
}
@@ -242,63 +245,51 @@ post_init_rtc_hook(void *anything)
}
/* ================================================== */
/* Return 1 if the process exists on the system. */
static int
does_process_exist(int pid)
{
int status;
status = getsid(pid);
if (status >= 0) {
return 1;
} else {
return 0;
}
}
/* ================================================== */
static int
maybe_another_chronyd_running(int *other_pid)
static void
check_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
FILE *in;
int pid, count;
*other_pid = 0;
in = fopen(pidfile, "r");
if (!in) return 0;
if (!in)
return;
count = fscanf(in, "%d", &pid);
fclose(in);
if (count != 1) return 0;
if (count != 1)
return;
*other_pid = pid;
return does_process_exist(pid);
if (getsid(pid) < 0)
return;
LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
pid, pidfile);
}
/* ================================================== */
static void
write_lockfile(void)
write_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
FILE *out;
out = fopen(pidfile, "w");
if (!out) {
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
} else {
fprintf(out, "%d\n", (int)getpid());
fclose(out);
}
if (!pidfile[0])
return;
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
fprintf(out, "%d\n", (int)getpid());
fclose(out);
}
/* ================================================== */
#define DEV_NULL "/dev/null"
static void
go_daemon(void)
{
@@ -307,14 +298,14 @@ go_daemon(void)
/* Create pipe which will the daemon use to notify the grandparent
when it's initialised or send an error message */
if (pipe(pipefd)) {
LOG_FATAL(LOGF_Main, "Could not detach, pipe failed : %s", strerror(errno));
LOG_FATAL("pipe() failed : %s", strerror(errno));
}
/* Does this preserve existing signal handlers? */
pid = fork();
if (pid < 0) {
LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
/* In the 'grandparent' */
char message[1024];
@@ -340,7 +331,7 @@ go_daemon(void)
pid = fork();
if (pid < 0) {
LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
exit(0); /* In the 'parent' */
} else {
@@ -348,7 +339,7 @@ go_daemon(void)
/* Change current directory to / */
if (chdir("/") < 0) {
LOG_FATAL(LOGF_Main, "Could not chdir to / : %s", strerror(errno));
LOG_FATAL("chdir() failed : %s", strerror(errno));
}
/* Don't keep stdin/out/err from before. But don't close
@@ -359,148 +350,199 @@ go_daemon(void)
}
LOG_SetParentFd(pipefd[1]);
/* Open /dev/null as new stdin/out/err */
errno = 0;
if (open(DEV_NULL, O_RDONLY) != STDIN_FILENO ||
open(DEV_NULL, O_WRONLY) != STDOUT_FILENO ||
open(DEV_NULL, O_RDWR) != STDERR_FILENO)
LOG_FATAL("Could not open %s : %s", DEV_NULL, strerror(errno));
}
}
}
/* ================================================== */
static void
print_help(const char *progname)
{
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
}
/* ================================================== */
static void
print_version(void)
{
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
}
/* ================================================== */
static int
parse_int_arg(const char *arg)
{
int i;
if (sscanf(arg, "%d", &i) != 1)
LOG_FATAL("Invalid argument %s", arg);
return i;
}
/* ================================================== */
int main
(int argc, char **argv)
{
const char *conf_file = DEFAULT_CONF_FILE;
const char *progname = argv[0];
char *user = NULL;
char *user = NULL, *log_file = NULL;
struct passwd *pw;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0, timeout = 0;
int other_pid;
int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int system_log = 1;
int clock_control = 1, system_log = 1;
int config_args = 0;
do_platform_checks();
LOG_Initialise();
/* Parse command line options */
while (++argv, (--argc)>0) {
if (!strcmp("-f", *argv)) {
++argv, --argc;
conf_file = *argv;
} else if (!strcmp("-P", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) {
LOG_FATAL(LOGF_Main, "Bad scheduler priority");
}
} else if (!strcmp("-m", *argv)) {
lock_memory = 1;
} else if (!strcmp("-r", *argv)) {
reload = 1;
} else if (!strcmp("-R", *argv)) {
restarted = 1;
} else if (!strcmp("-u", *argv)) {
++argv, --argc;
if (argc == 0) {
LOG_FATAL(LOGF_Main, "Missing user name");
} else {
user = *argv;
}
} else if (!strcmp("-F", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &scfilter_level) != 1)
LOG_FATAL(LOGF_Main, "Bad syscall filter level");
} else if (!strcmp("-s", *argv)) {
do_init_rtc = 1;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
/* Parse (undocumented) long command-line options */
for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) {
print_help(progname);
return 0;
} else if (!strcmp("-n", *argv)) {
nofork = 1;
} else if (!strcmp("-d", *argv)) {
debug++;
nofork = 1;
system_log = 0;
} else if (!strcmp("-q", *argv)) {
ref_mode = REF_ModeUpdateOnce;
nofork = 1;
system_log = 0;
} else if (!strcmp("-Q", *argv)) {
ref_mode = REF_ModePrintOnce;
nofork = 1;
system_log = 0;
} else if (!strcmp("-t", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &timeout) != 1 || timeout <= 0)
LOG_FATAL(LOGF_Main, "Bad timeout");
} else if (!strcmp("-4", *argv)) {
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
} else if (!strcmp("--version", argv[optind])) {
print_version();
return 0;
} else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else {
/* Process remaining arguments and configuration lines */
config_args = argc;
break;
}
}
if (getuid() != 0) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
fprintf(stderr,"Not superuser\n");
return 1;
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
switch (opt) {
case '4':
case '6':
address_family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
break;
case 'd':
debug++;
nofork = 1;
system_log = 0;
break;
case 'f':
conf_file = optarg;
break;
case 'F':
scfilter_level = parse_int_arg(optarg);
break;
case 'l':
log_file = optarg;
break;
case 'm':
lock_memory = 1;
break;
case 'n':
nofork = 1;
break;
case 'P':
sched_priority = parse_int_arg(optarg);
break;
case 'q':
ref_mode = REF_ModeUpdateOnce;
nofork = 1;
client_only = 0;
system_log = 0;
break;
case 'Q':
ref_mode = REF_ModePrintOnce;
nofork = 1;
client_only = 1;
clock_control = 0;
system_log = 0;
break;
case 'r':
reload = 1;
break;
case 'R':
restarted = 1;
break;
case 's':
do_init_rtc = 1;
break;
case 't':
timeout = parse_int_arg(optarg);
break;
case 'u':
user = optarg;
break;
case 'v':
print_version();
return 0;
case 'x':
clock_control = 0;
break;
default:
print_help(progname);
return opt != 'h';
}
}
if (getuid() && !client_only)
LOG_FATAL("Not superuser");
/* Turn into a daemon */
if (!nofork) {
go_daemon();
}
if (system_log) {
if (log_file) {
LOG_OpenFileLog(log_file);
} else if (system_log) {
LOG_OpenSystemLog();
}
LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)",
CHRONY_VERSION, CHRONYD_FEATURES);
LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted);
CNF_Initialise(restarted, client_only);
/* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
if (!config_args) {
CNF_ReadFile(conf_file);
} else {
do {
CNF_ParseLine(NULL, config_args - argc + 1, *argv);
} while (++argv, --argc);
for (; optind < argc; optind++)
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
}
/* Check whether another chronyd may already be running. Do this after
* forking, so that message logging goes to the right place (i.e. syslog), in
* case this chronyd is being run from a boot script. */
if (maybe_another_chronyd_running(&other_pid)) {
LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)",
other_pid, CNF_GetPidFile());
}
/* Check whether another chronyd may already be running */
check_pidfile();
/* Write our lockfile to prevent other chronyds running. This has *GOT* to
* be done *AFTER* the daemon-creation fork() */
write_lockfile();
if (!user)
user = CNF_GetUser();
pw = getpwnam(user);
if (!pw)
LOG_FATAL("Could not get user/group ID of %s", user);
/* Create directories for sockets, log files, and dump files */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Write our pidfile to prevent other instances from running */
write_pidfile();
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
SYS_Initialise();
SYS_Initialise(clock_control);
RTC_Initialise(do_init_rtc);
SRC_Initialise();
RCL_Initialise();
@@ -524,18 +566,8 @@ int main
SYS_LockMemory();
}
if (!user) {
user = CNF_GetUser();
}
if ((pw = getpwnam(user)) == NULL)
LOG_FATAL(LOGF_Main, "Could not get %s uid/gid", user);
/* Create all directories before dropping root */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Drop root privileges if the user has non-zero uid or gid */
if (pw->pw_uid || pw->pw_gid)
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
REF_Initialise();
@@ -550,7 +582,7 @@ int main
/* From now on, it is safe to do finalisation on exit */
initialised = 1;
UTI_SetQuitSignalsHandler(signal_cleanup);
UTI_SetQuitSignalsHandler(signal_cleanup, 1);
CAM_OpenUnixSocket();
@@ -564,7 +596,7 @@ int main
REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode);
if (timeout)
if (timeout > 0)
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
if (do_init_rtc) {
@@ -577,7 +609,7 @@ int main
the scheduler. */
SCH_MainLoop();
LOG(LOGS_INFO, LOGF_Main, "chronyd exiting");
LOG(LOGS_INFO, "chronyd exiting");
MAI_CleanupAndExit();

View File

@@ -36,8 +36,6 @@ cd RELEASES/$subdir || exit 1
echo $version > version.txt
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
./configure && make -C doc man txt || exit 1
iconv -f utf-8 -t ascii//TRANSLIT < doc/installation.txt > INSTALL

View File

@@ -97,7 +97,8 @@ MNL_Finalise(void)
/* ================================================== */
static void
estimate_and_set_system(struct timespec *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
estimate_and_set_system(struct timespec *now, int offset_provided, double offset,
double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
{
double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
double b0, b1;
@@ -108,32 +109,26 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
int found_freq;
double slew_by;
b0 = offset_provided ? offset : 0.0;
b1 = freq = 0.0;
found_freq = 0;
if (n_samples > 1) {
for (i=0; i<n_samples; i++) {
agos[i] = UTI_DiffTimespecsToDouble(&samples[n_samples - 1].when, &samples[i].when);
offsets[i] = samples[i].offset;
}
RGR_FindBestRobustRegression(agos, offsets, n_samples,
1.0e-8, /* 0.01ppm easily good enough for this! */
&b0, &b1, &n_runs, &best_start);
/* Ignore b0 from regression; treat offset as being the most
recently entered value. (If the administrator knows he's put
an outlier in, he will rerun the settime operation.) However,
the frequency estimate comes from the regression. */
freq = -b1;
found_freq = 1;
} else {
if (offset_provided) {
b0 = offset;
} else {
b0 = 0.0;
if (RGR_FindBestRobustRegression(agos, offsets, n_samples, 1.0e-8,
&b0, &b1, &n_runs, &best_start)) {
/* Ignore b0 from regression; treat offset as being the most
recently entered value. (If the administrator knows he's put
an outlier in, he will rerun the settime operation.) However,
the frequency estimate comes from the regression. */
freq = -b1;
found_freq = 1;
}
b1 = freq = 0.0;
found_freq = 0;
} else {
agos[0] = 0.0;
offsets[0] = b0;
}
@@ -145,21 +140,20 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
}
if (found_freq) {
LOG(LOGS_INFO, LOGF_Manual,
"Making a frequency change of %.3f ppm and a slew of %.6f",
LOG(LOGS_INFO, "Making a frequency change of %.3f ppm and a slew of %.6f",
1.0e6 * freq, slew_by);
REF_SetManualReference(now,
slew_by,
freq, skew);
} else {
LOG(LOGS_INFO, LOGF_Manual, "Making a slew of %.6f", slew_by);
LOG(LOGS_INFO, "Making a slew of %.6f", slew_by);
REF_SetManualReference(now,
slew_by,
0.0, skew);
}
if (offset_cs) *offset_cs = (long)(0.5 + 100.0 * b0);
if (reg_offset) *reg_offset = b0;
if (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq;
if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency();
@@ -173,7 +167,7 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
/* ================================================== */
int
MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
{
struct timespec now;
double offset, diff;
@@ -210,7 +204,7 @@ MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, dou
samples[n_samples].orig_offset = offset;
++n_samples;
estimate_and_set_system(&now, 1, offset, offset_cs, dfreq_ppm, new_afreq_ppm);
estimate_and_set_system(&now, 1, offset, reg_offset, dfreq_ppm, new_afreq_ppm);
return 1;

View File

@@ -33,7 +33,7 @@
extern void MNL_Initialise(void);
extern void MNL_Finalise(void);
extern int MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
extern int MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm);
extern void MNL_Enable(void);
extern void MNL_Disable(void);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
* Copyright (C) Miroslav Lichvar 2014, 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -37,7 +37,7 @@ Malloc(size_t size)
r = malloc(size);
if (!r && size)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}
@@ -49,11 +49,37 @@ Realloc(void *ptr, size_t size)
r = realloc(ptr, size);
if (!r && size)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}
static size_t
get_array_size(size_t nmemb, size_t size)
{
size_t array_size;
array_size = nmemb * size;
/* Check for overflow */
if (nmemb > 0 && array_size / nmemb != size)
LOG_FATAL("Could not allocate memory");
return array_size;
}
void *
Malloc2(size_t nmemb, size_t size)
{
return Malloc(get_array_size(nmemb, size));
}
void *
Realloc2(void *ptr, size_t nmemb, size_t size)
{
return Realloc(ptr, get_array_size(nmemb, size));
}
char *
Strdup(const char *s)
{
@@ -61,7 +87,7 @@ Strdup(const char *s)
r = strdup(s);
if (!r)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}

View File

@@ -27,15 +27,19 @@
#ifndef GOT_MEMORY_H
#define GOT_MEMORY_H
#include "sysincl.h"
/* Wrappers checking for errors */
extern void *Malloc(size_t size);
extern void *Realloc(void *ptr, size_t size);
extern void *Malloc2(size_t nmemb, size_t size);
extern void *Realloc2(void *ptr, size_t nmemb, size_t size);
extern char *Strdup(const char *s);
/* Convenient macros */
#define MallocNew(T) ((T *) Malloc(sizeof(T)))
#define MallocArray(T, n) ((T *) Malloc((n) * sizeof(T)))
#define ReallocArray(T,n,x) ((T *) Realloc((void *)(x), (n)*sizeof(T)))
#define MallocArray(T, n) ((T *) Malloc2(n, sizeof(T)))
#define ReallocArray(T, n, x) ((T *) Realloc2((void *)(x), n, sizeof(T)))
#define Free(x) free(x)
#endif /* GOT_MEMORY_H */

View File

@@ -30,6 +30,9 @@
#include "sysincl.h"
#include <netdb.h>
#include <resolv.h>
#include "nameserv.h"
#include "util.h"
@@ -53,7 +56,19 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
memset(&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC;
switch (address_family) {
case IPADDR_INET4:
hints.ai_family = AF_INET;
break;
#ifdef FEAT_IPV6
case IPADDR_INET6:
hints.ai_family = AF_INET6;
break;
#endif
default:
hints.ai_family = AF_UNSPEC;
}
hints.ai_socktype = SOCK_STREAM;
result = getaddrinfo(name, NULL, &hints, &res);

View File

@@ -78,7 +78,7 @@ end_resolving(int fd, int event, void *anything)
int i;
if (pthread_join(inst->thread, NULL)) {
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
LOG_FATAL("pthread_join() failed");
}
resolving_threads--;
@@ -110,7 +110,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->status = DNS_Failure;
if (pipe(inst->pipe)) {
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
LOG_FATAL("pipe() failed");
}
UTI_FdSetCloexec(inst->pipe[0]);
@@ -120,7 +120,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
assert(resolving_threads <= 1);
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
LOG_FATAL("pthread_create() failed");
}
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst);

18
ntp.h
View File

@@ -38,6 +38,9 @@ typedef struct {
typedef uint32_t NTP_int32;
/* The UDP port number used by NTP */
#define NTP_PORT 123
/* The NTP protocol version that we support */
#define NTP_VERSION 4
@@ -118,4 +121,19 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Structure used to save NTP measurements. time is the local time at which
the sample is to be considered to have been made and offset is the offset at
the time (positive indicates that the local clock is slow relative to the
source). root_delay/root_dispersion include peer_delay/peer_dispersion. */
typedef struct {
struct timespec time;
double offset;
double peer_delay;
double peer_dispersion;
double root_delay;
double root_dispersion;
int stratum;
NTP_Leap leap;
} NTP_Sample;
#endif /* GOT_NTP_H */

File diff suppressed because it is too large Load Diff

View File

@@ -99,12 +99,9 @@ extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Addr
/* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
/* Take a particular source online (i.e. start sampling it) */
extern void NCR_TakeSourceOnline(NCR_Instance inst);
/* Take a particular source offline (i.e. stop sampling it, without
marking it unreachable in the source selection stuff) */
extern void NCR_TakeSourceOffline(NCR_Instance inst);
/* Take a particular source online (i.e. start sampling it) or offline
(i.e. stop sampling it) */
extern void NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity);
extern void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll);

170
ntp_io.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2015
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018
*
* 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
@@ -105,6 +105,9 @@ static int separate_client_sockets;
disabled */
static int permanent_server_sockets;
/* Flag indicating the server IPv4 socket is bound to an address */
static int bound_server_sock_fd4;
/* Flag indicating that we have been initialised */
static int initialised=0;
@@ -130,10 +133,10 @@ prepare_socket(int family, int port_number, int client_only)
if (sock_fd < 0) {
if (!client_only) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
LOG(LOGS_ERR, "Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
} else {
DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s",
DEBUG_LOG("Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
}
return INVALID_SOCK_FD;
@@ -142,6 +145,10 @@ prepare_socket(int family, int port_number, int client_only)
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
/* Enable non-blocking mode on server sockets */
if (!client_only && fcntl(sock_fd, F_SETFL, O_NONBLOCK))
DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
/* Prepare local address */
memset(&my_addr, 0, sizeof (my_addr));
my_addr_len = 0;
@@ -164,6 +171,9 @@ prepare_socket(int family, int port_number, int client_only)
my_addr.in4.sin_port = htons(port_number);
my_addr_len = sizeof (my_addr.in4);
if (!client_only)
bound_server_sock_fd4 = my_addr.in4.sin_addr.s_addr != htonl(INADDR_ANY);
break;
#ifdef FEAT_IPV6
case AF_INET6:
@@ -193,48 +203,45 @@ prepare_socket(int family, int port_number, int client_only)
/* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_REUSEADDR");
LOG(LOGS_ERR, "Could not set %s socket option", "SO_REUSEADDR");
/* Don't quit - we might survive anyway */
}
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_BROADCAST");
LOG(LOGS_ERR, "Could not set %s socket option", "SO_BROADCAST");
/* Don't quit - we might survive anyway */
}
#ifdef SO_TIMESTAMP
/* Enable receiving of timestamp control messages */
#ifdef SO_TIMESTAMPNS
/* Try nanosecond resolution first */
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
#endif
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP");
/* Don't quit - we might survive anyway */
}
#endif
/* Enable kernel/HW timestamping of packets */
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events);
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
#endif
#ifdef SO_TIMESTAMPNS
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
#endif
#ifdef SO_TIMESTAMP
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP");
#endif
;
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_FREEBIND");
LOG(LOGS_ERR, "Could not set %s socket option", "IP_FREEBIND");
}
#endif
if (family == AF_INET) {
#ifdef HAVE_IN_PKTINFO
/* We want the local IP info on server sockets */
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_PKTINFO");
/* Don't quit - we might survive anyway */
}
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO");
#elif defined(IP_RECVDSTADDR)
if (setsockopt(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "IP_RECVDSTADDR");
#endif
}
#ifdef FEAT_IPV6
@@ -242,18 +249,18 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_V6ONLY");
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_V6ONLY");
}
#endif
#ifdef HAVE_IN6_PKTINFO
#ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_RECVPKTINFO");
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_RECVPKTINFO");
}
#else
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_PKTINFO");
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_PKTINFO");
}
#endif
#endif
@@ -262,7 +269,7 @@ prepare_socket(int family, int port_number, int client_only)
/* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
LOG(LOGS_ERR, "Could not bind %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd);
return INVALID_SOCK_FD;
@@ -304,7 +311,7 @@ connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
assert(addr_len);
if (connect(sock_fd, &addr.u, addr_len) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
DEBUG_LOG("Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
@@ -321,6 +328,9 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
}
@@ -365,10 +375,9 @@ NIO_Initialise(int family)
NIO_Linux_Initialise();
#else
if (1) {
double tx_comp, rx_comp;
char *name;
if (CNF_GetHwTsInterface(0, &name, &tx_comp, &rx_comp))
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
CNF_HwTsInterface *conf_iface;
if (CNF_GetHwTsInterface(0, &conf_iface))
LOG_FATAL("HW timestamping not supported");
}
#endif
@@ -431,7 +440,7 @@ NIO_Initialise(int family)
&& client_sock_fd6 == INVALID_SOCK_FD
#endif
)) {
LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets");
LOG_FATAL("Could not open NTP sockets");
}
}
@@ -571,6 +580,23 @@ NIO_IsServerSocket(int sock_fd)
/* ================================================== */
int
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
{
int sock_fd, r;
sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
if (sock_fd == INVALID_SOCK_FD)
return 0;
r = connect_socket(sock_fd, remote_addr);
close_socket(sock_fd);
return r;
}
/* ================================================== */
static void
process_message(struct msghdr *hdr, int length, int sock_fd)
{
@@ -579,14 +605,13 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
NTP_Local_Timestamp local_ts;
struct timespec sched_ts;
struct cmsghdr *cmsg;
int if_index;
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON;
sched_ts = local_ts.ts;
if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
DEBUG_LOG(LOGF_NtpIO, "Truncated source address");
DEBUG_LOG("Truncated source address");
return;
}
@@ -599,17 +624,17 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
}
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = sock_fd;
if_index = -1;
if (hdr->msg_flags & MSG_TRUNC) {
DEBUG_LOG(LOGF_NtpIO, "Received truncated message from %s:%d",
DEBUG_LOG("Received truncated message from %s:%d",
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
return;
}
if (hdr->msg_flags & MSG_CTRUNC) {
DEBUG_LOG(LOGF_NtpIO, "Truncated control message");
DEBUG_LOG("Truncated control message");
/* Continue */
}
@@ -621,7 +646,15 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
if_index = ipi.ipi_ifindex;
local_addr.if_index = ipi.ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR)
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
struct in_addr addr;
memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr));
local_addr.ip_addr.addr.in4 = ntohl(addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
}
#endif
@@ -633,7 +666,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6;
if_index = ipi.ipi6_ifindex;
local_addr.if_index = ipi.ipi6_ifindex;
}
#endif
@@ -661,14 +694,13 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
}
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts,
hdr, length, sock_fd, if_index))
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length))
return;
#endif
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%u delay=%.9f",
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, if_index,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
/* Just ignore the packet if it's not of a recognized length */
@@ -691,6 +723,11 @@ read_from_socket(int sock_fd, int event, void *anything)
unsigned int i, n;
int status, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event))
return;
#endif
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
assert(n >= 1);
@@ -715,7 +752,21 @@ read_from_socket(int sock_fd, int event, void *anything)
#endif
if (status < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not receive from fd %d : %s", sock_fd,
#ifdef HAVE_LINUX_TIMESTAMPING
/* If reading from the error queue failed, the exception should be
for a socket error. Clear the error to avoid a busy loop. */
if (flags & MSG_ERRQUEUE) {
int error = 0;
socklen_t len = sizeof (error);
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len))
DEBUG_LOG("Could not get SO_ERROR");
if (error)
errno = error;
}
#endif
DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
strerror(errno));
return;
}
@@ -746,7 +797,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
assert(initialised);
if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
DEBUG_LOG("No socket to send to %s:%d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
return 0;
}
@@ -790,6 +841,25 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
if (local_addr->if_index != INVALID_IF_INDEX)
ipi->ipi_ifindex = local_addr->if_index;
}
#elif defined(IP_SENDSRCADDR)
/* Specify the IPv4 source address only if the socket is not bound */
if (local_addr->ip_addr.family == IPADDR_INET4 &&
local_addr->sock_fd == server_sock_fd4 && !bound_server_sock_fd4) {
struct in_addr *addr;
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof (struct in_addr)));
cmsglen += CMSG_SPACE(sizeof (struct in_addr));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_addr));
addr = (struct in_addr *)CMSG_DATA(cmsg);
addr->s_addr = htonl(local_addr->ip_addr.addr.in4);
}
#endif
@@ -808,6 +878,8 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
sizeof(ipi->ipi6_addr.s6_addr));
if (local_addr->if_index != INVALID_IF_INDEX)
ipi->ipi6_ifindex = local_addr->if_index;
}
#endif
@@ -822,14 +894,14 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
msg.msg_control = NULL;
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
DEBUG_LOG("Could not send to %s:%d from %s fd %d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
strerror(errno));
return 0;
}
DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", length,
DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length,
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);

View File

@@ -53,6 +53,9 @@ extern void NIO_CloseServerSocket(int sock_fd);
/* Function to check if socket is a server socket */
extern int NIO_IsServerSocket(int sock_fd);
/* Function to check if client packets can be sent to a server */
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2017
* Copyright (C) Miroslav Lichvar 2016-2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -32,7 +32,6 @@
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock.h>
#include <linux/sockios.h>
#include <net/if.h>
@@ -61,11 +60,15 @@ struct Interface {
char name[IF_NAMESIZE];
int if_index;
int phc_fd;
int phc_mode;
int phc_nocrossts;
/* Link speed in mbit/s */
int link_speed;
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
@@ -75,6 +78,9 @@ struct Interface {
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
/* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
#define MAX_TS_DELAY 1.0
@@ -88,22 +94,42 @@ static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* When sending client requests to a close and fast server, it is possible that
a response will be received before the HW transmit timestamp of the request
itself. To avoid processing of the response without the HW timestamp, we
monitor events returned by select() and suspend reading of packets from the
receive queue for up to 200 microseconds. As the requests are normally
separated by at least 200 milliseconds, it is sufficient to monitor and
suspend one socket at a time. */
static int monitored_socket;
static int suspended_socket;
static SCH_TimeoutID resume_timeout_id;
#define RESUME_TIMEOUT 200.0e-6
/* Unbound socket keeping the kernel RX timestamping permanently enabled
in order to avoid a race condition between receiving a server response
and the kernel actually starting to timestamp received packets after
enabling the timestamping and sending a request */
static int dummy_rxts_socket;
#define INVALID_SOCK_FD -3
/* ================================================== */
static int
add_interface(const char *name, double tx_comp, double rx_comp)
add_interface(CNF_HwTsInterface *conf_iface)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_index, phc_fd;
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
unsigned int i;
struct Interface *iface;
char phc_path[64];
/* Check if the interface was not already added */
for (i = 0; i < ARR_GetSize(interfaces); i++) {
if (!strcmp(name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
if (!strcmp(conf_iface->name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
return 1;
}
@@ -114,13 +140,14 @@ add_interface(const char *name, double tx_comp, double rx_comp)
memset(&req, 0, sizeof (req));
memset(&ts_info, 0, sizeof (ts_info));
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", name) >= sizeof (req.ifr_name)) {
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
close(sock_fd);
return 0;
}
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
close(sock_fd);
return 0;
}
@@ -131,53 +158,100 @@ add_interface(const char *name, double tx_comp, double rx_comp)
req.ifr_data = (char *)&ts_info;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return 0;
}
req_hwts_flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
close(sock_fd);
return 0;
}
if (ts_info.phc_index < 0) {
DEBUG_LOG("PHC missing on %s", req.ifr_name);
close(sock_fd);
return 0;
}
switch (conf_iface->rxfilter) {
case CNF_HWTS_RXFILTER_ANY:
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_NTP_ALL))
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
else
#endif
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))
rx_filter = HWTSTAMP_FILTER_ALL;
else
rx_filter = HWTSTAMP_FILTER_NONE;
break;
case CNF_HWTS_RXFILTER_NONE:
rx_filter = HWTSTAMP_FILTER_NONE;
break;
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
case CNF_HWTS_RXFILTER_NTP:
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break;
#endif
default:
rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON;
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
ts_config.rx_filter = rx_filter;
req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
close(sock_fd);
return 0;
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
/* Check the current timestamping configuration in case this interface
allows only reading of the configuration and it was already configured
as requested */
req.ifr_data = (char *)&ts_config;
#ifdef SIOCGHWTSTAMP
if (ioctl(sock_fd, SIOCGHWTSTAMP, &req) ||
ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter)
#endif
{
close(sock_fd);
return 0;
}
}
close(sock_fd);
phc_index = ts_info.phc_index;
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
return 0;
phc_fd = open(phc_path, O_RDONLY);
if (phc_fd < 0) {
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not open %s : %s", phc_path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(phc_fd);
iface = ARR_GetNewElement(interfaces);
snprintf(iface->name, sizeof (iface->name), "%s", name);
snprintf(iface->name, sizeof (iface->name), "%s", conf_iface->name);
iface->if_index = if_index;
iface->phc_fd = phc_fd;
iface->phc_mode = 0;
iface->phc_nocrossts = conf_iface->nocrossts;
/* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
iface->link_speed = 1000;
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->tx_comp = tx_comp;
iface->rx_comp = rx_comp;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
iface->clock = HCL_CreateInstance();
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", name);
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
return 1;
}
@@ -185,18 +259,22 @@ add_interface(const char *name, double tx_comp, double rx_comp)
/* ================================================== */
static int
add_all_interfaces(double tx_comp, double rx_comp)
add_all_interfaces(CNF_HwTsInterface *conf_iface_all)
{
CNF_HwTsInterface conf_iface;
struct ifaddrs *ifaddr, *ifa;
int r;
conf_iface = *conf_iface_all;
if (getifaddrs(&ifaddr)) {
DEBUG_LOG(LOGF_NtpIOLinux, "getifaddrs() failed : %s", strerror(errno));
DEBUG_LOG("getifaddrs() failed : %s", strerror(errno));
return 0;
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (add_interface(ifa->ifa_name, tx_comp, rx_comp))
conf_iface.name = ifa->ifa_name;
if (add_interface(&conf_iface))
r = 1;
}
@@ -213,7 +291,7 @@ update_interface_speed(struct Interface *iface)
{
struct ethtool_cmd cmd;
struct ifreq req;
int sock_fd;
int sock_fd, link_speed;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
@@ -227,14 +305,65 @@ update_interface_speed(struct Interface *iface)
req.ifr_data = (char *)&cmd;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return;
}
close(sock_fd);
iface->link_speed = ethtool_cmd_speed(&cmd);
link_speed = ethtool_cmd_speed(&cmd);
if (iface->link_speed != link_speed) {
iface->link_speed = link_speed;
DEBUG_LOG("Updated speed of %s to %d Mb/s", iface->name, link_speed);
}
}
/* ================================================== */
#if defined(HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO) || defined(HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW)
static int
check_timestamping_option(int option)
{
int sock_fd;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option);
close(sock_fd);
return 0;
}
close(sock_fd);
return 1;
}
#endif
/* ================================================== */
static int
open_dummy_socket(void)
{
int sock_fd, events = 0;
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
#ifdef FEAT_IPV6
&& (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
#endif
)
return INVALID_SOCK_FD;
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
close(sock_fd);
return INVALID_SOCK_FD;
}
UTI_FdSetCloexec(sock_fd);
return sock_fd;
}
/* ================================================== */
@@ -242,8 +371,7 @@ update_interface_speed(struct Interface *iface)
void
NIO_Linux_Initialise(void)
{
double tx_comp, rx_comp;
char *name;
CNF_HwTsInterface *conf_iface;
unsigned int i;
int hwts;
@@ -252,28 +380,36 @@ NIO_Linux_Initialise(void)
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
for (i = hwts = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (!strcmp("*", name))
for (i = hwts = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (!strcmp("*", conf_iface->name))
continue;
if (!add_interface(name, tx_comp, rx_comp))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", name);
if (!add_interface(conf_iface))
LOG_FATAL("Could not enable HW timestamping on %s", conf_iface->name);
hwts = 1;
}
for (i = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (strcmp("*", name))
for (i = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (strcmp("*", conf_iface->name))
continue;
if (add_all_interfaces(tx_comp, rx_comp))
if (add_all_interfaces(conf_iface))
hwts = 1;
break;
}
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
if (hwts) {
ts_flags = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_HARDWARE;
} else {
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
ts_flags |= SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ts_tx_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_PKTINFO))
ts_flags |= SOF_TIMESTAMPING_OPT_PKTINFO;
#endif
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_TX_SWHW))
ts_flags |= SOF_TIMESTAMPING_OPT_TX_SWHW;
#endif
}
/* Enable IP_PKTINFO in messages looped back to the error queue */
@@ -281,6 +417,10 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
monitored_socket = INVALID_SOCK_FD;
suspended_socket = INVALID_SOCK_FD;
dummy_rxts_socket = INVALID_SOCK_FD;
}
/* ================================================== */
@@ -291,6 +431,9 @@ NIO_Linux_Finalise(void)
struct Interface *iface;
unsigned int i;
if (dummy_rxts_socket != INVALID_SOCK_FD)
close(dummy_rxts_socket);
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock);
@@ -320,13 +463,13 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
flags |= ts_tx_flags;
if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
ts_flags = 0;
return 0;
}
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_TIMESTAMPING");
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
ts_flags = 0;
return 0;
}
@@ -337,64 +480,69 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
static int
get_phc_sample(int phc_fd, struct timespec *phc_ts, struct timespec *local_ts, double *p_delay)
static void
resume_socket(int sock_fd)
{
struct ptp_sys_offset sys_off;
struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
double min_delay = 0.0, delays[PHC_READINGS], phc_sum, local_sum, local_prec;
int i, n;
if (monitored_socket == sock_fd)
monitored_socket = INVALID_SOCK_FD;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
return;
sys_off.n_samples = PHC_READINGS;
suspended_socket = INVALID_SOCK_FD;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
resume_timeout_id ? "before" : "on", sock_fd);
if (resume_timeout_id) {
SCH_RemoveTimeout(resume_timeout_id);
resume_timeout_id = 0;
}
}
/* ================================================== */
static void
resume_timeout(void *arg)
{
resume_timeout_id = 0;
resume_socket(suspended_socket);
}
/* ================================================== */
static void
suspend_socket(int sock_fd)
{
resume_socket(suspended_socket);
suspended_socket = sock_fd;
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
}
/* ================================================== */
int
NIO_Linux_ProcessEvent(int sock_fd, int event)
{
if (sock_fd != monitored_socket)
return 0;
if (event == SCH_FILE_INPUT) {
suspend_socket(monitored_socket);
monitored_socket = INVALID_SOCK_FD;
/* Don't process the message yet */
return 1;
}
for (i = 0; i < PHC_READINGS; i++) {
ts1.tv_sec = sys_off.ts[i * 2].sec;
ts1.tv_nsec = sys_off.ts[i * 2].nsec;
ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
sys_tss[i] = ts1;
phc_tss[i] = ts2;
delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
if (delays[i] <= 0.0)
/* Step in the middle of a PHC reading? */
return 0;
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
local_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = n = 0, phc_sum = local_sum = 0.0; i < PHC_READINGS; i++) {
if (delays[i] > min_delay + local_prec)
continue;
phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
local_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
n++;
}
assert(n);
UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
UTI_AddDoubleToTimespec(&sys_tss[0], local_sum / n, &ts1);
LCL_CookTime(&ts1, local_ts, NULL);
*p_delay = min_delay;
return 1;
return 0;
}
/* ================================================== */
@@ -420,52 +568,60 @@ get_interface(int if_index)
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family)
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
struct timespec sample_phc_ts, sample_local_ts, ts;
double sample_delay, rx_correction, ts_delay, err;
int l2_length;
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double rx_correction, ts_delay, phc_err, local_err;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!get_phc_sample(iface->phc_fd, &sample_phc_ts, &sample_local_ts, &sample_delay))
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
sample_delay / 2.0);
phc_err + local_err);
update_interface_speed(iface);
}
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
timestamps. Without raw sockets we don't know the length of the packet
at layer 2, so we make an assumption that UDP data start at the same
position as in the last transmitted packet which had a HW TX timestamp. */
timestamps. If we don't know the length of the packet at layer 2, we
make an assumption that UDP data start at the same position as in the
last transmitted packet which had a HW TX timestamp. */
if (rx_ntp_length && iface->link_speed) {
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
if (!l2_length)
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
iface->l2_udp6_ntp_start) + rx_ntp_length;
/* Include the frame check sequence (FCS) */
l2_length += 4;
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
}
if (!rx_ntp_length && iface->tx_comp)
UTI_AddDoubleToTimespec(hw_ts, iface->tx_comp, hw_ts);
else if (rx_ntp_length && iface->rx_comp)
UTI_AddDoubleToTimespec(hw_ts, -iface->rx_comp, hw_ts);
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &err))
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &local_err))
return;
if (!rx_ntp_length && iface->tx_comp)
UTI_AddDoubleToTimespec(&ts, iface->tx_comp, &ts);
else if (rx_ntp_length && iface->rx_comp)
UTI_AddDoubleToTimespec(&ts, -iface->rx_comp, &ts);
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG(LOGF_NtpIOLinux, "Unacceptable timestamp delay %.9f", ts_delay);
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = err;
local_ts->err = local_err;
local_ts->source = NTP_TS_HARDWARE;
}
@@ -510,14 +666,43 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
len -= ihl + 8, msg += ihl + 8;
#ifdef FEAT_IPV6
} else if (len >= 48 && msg[0] >> 4 == 6) {
/* IPv6 extension headers are not supported */
if (msg[6] != 17)
return 0;
int eh_len, next_header = msg[6];
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
addr.in6.sin6_port = *(uint16_t *)(msg + 40 + 2);
len -= 40, msg += 40;
/* Skip IPv6 extension headers if present */
while (next_header != 17) {
switch (next_header) {
case 44: /* Fragment Header */
/* Process only the first fragment */
if (ntohs(*(uint16_t *)(msg + 2)) >> 3 != 0)
return 0;
eh_len = 8;
break;
case 0: /* Hop-by-Hop Options */
case 43: /* Routing Header */
case 60: /* Destination Options */
case 135: /* Mobility Header */
eh_len = 8 * (msg[1] + 1);
break;
case 51: /* Authentication Header */
eh_len = 4 * (msg[1] + 2);
break;
default:
return 0;
}
if (eh_len < 8 || len < eh_len + 8)
return 0;
next_header = msg[0];
len -= eh_len, msg += eh_len;
}
addr.in6.sin6_port = *(uint16_t *)(msg + 2);
addr.in6.sin6_family = AF_INET6;
len -= 48, msg += 48;
len -= 8, msg += 8;
#endif
} else {
return 0;
@@ -536,33 +721,55 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
int
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr,
int length, int sock_fd, int if_index)
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
{
struct Interface *iface;
struct cmsghdr *cmsg;
int is_tx, l2_length;
int is_tx, ts_if_index, l2_length;
is_tx = hdr->msg_flags & MSG_ERRQUEUE;
iface = NULL;
ts_if_index = local_addr->if_index;
l2_length = 0;
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) {
struct scm_ts_pktinfo ts_pktinfo;
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
ts_if_index = ts_pktinfo.if_index;
l2_length = ts_pktinfo.pkt_length;
DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length);
}
#endif
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
if (!UTI_IsZeroTimespec(&ts3.ts[0])) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
} else if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(if_index);
if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(ts_if_index);
if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
remote_addr->ip_addr.family);
remote_addr->ip_addr.family, l2_length);
} else {
DEBUG_LOG(LOGF_NtpIOLinux, "HW clock not found for interface %d", if_index);
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
/* If a HW transmit timestamp was received, resume processing
of non-error messages on this socket */
if (is_tx)
resume_socket(local_addr->sock_fd);
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
(!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
}
}
@@ -574,13 +781,21 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
DEBUG_LOG(LOGF_NtpIOLinux, "Unknown extended error");
DEBUG_LOG("Unknown extended error");
/* Drop the message */
return 1;
}
}
}
/* If the kernel is slow with enabling RX timestamping, open a dummy
socket to keep the kernel RX timestamping permanently enabled */
if (!is_tx && local_ts->source == NTP_TS_DAEMON && ts_flags) {
DEBUG_LOG("Missing kernel RX timestamp");
if (dummy_rxts_socket == INVALID_SOCK_FD)
dummy_rxts_socket = open_dummy_socket();
}
/* Return the message if it's not received from the error queue */
if (!is_tx)
return 0;
@@ -591,9 +806,9 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
DEBUG_LOG(LOGF_NtpIOLinux, "Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%u",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
sock_fd, if_index, local_ts->source);
local_addr->sock_fd, local_addr->if_index, local_ts->source);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && length) {
@@ -603,9 +818,9 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
iface->l2_udp6_ntp_start = l2_length - length;
}
/* Drop the message if HW timestamp is missing or its processing failed */
if ((ts_flags & SOF_TIMESTAMPING_RAW_HARDWARE) && local_ts->source != NTP_TS_HARDWARE) {
DEBUG_LOG(LOGF_NtpIOLinux, "Missing HW timestamp");
/* Drop the message if it has no timestamp or its processing failed */
if (local_ts->source == NTP_TS_DAEMON) {
DEBUG_LOG("Missing TX timestamp");
return 1;
}
@@ -625,6 +840,15 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
if (!ts_flags)
return cmsglen;
/* If a HW transmit timestamp is requested on a client socket, monitor
events on the socket in order to avoid processing of a fast response
without the HW timestamp of the request */
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
monitored_socket = sock_fd;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen;
@@ -632,7 +856,12 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
/* Add control message that will enable TX timestamping for this message.
Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
control messages. */
cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen);
cmsg = CMSG_FIRSTHDR(msg);
if (!cmsg || cmsglen + CMSG_SPACE(sizeof (ts_tx_flags)) > msg->msg_controllen)
return cmsglen;
cmsg = (struct cmsghdr *)((char *)cmsg + cmsglen);
memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
@@ -644,3 +873,11 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
return cmsglen;
}
/* ================================================== */
void
NIO_Linux_NotifySocketClosing(int sock_fd)
{
resume_socket(sock_fd);
}

View File

@@ -24,14 +24,22 @@
This is the header file for the Linux-specific NTP socket I/O bits.
*/
#ifndef GOT_NTP_IO_LINUX_H
#define GOT_NTP_IO_LINUX_H
extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length,
int sock_fd, int if_index);
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
#endif

View File

@@ -135,7 +135,7 @@ open_socket(void)
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
DEBUG_LOG(LOGF_NtpSignd, "Could not open signd socket : %s", strerror(errno));
DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
return 0;
}
@@ -145,18 +145,18 @@ open_socket(void)
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
DEBUG_LOG(LOGF_NtpSignd, "signd socket path too long");
DEBUG_LOG("signd socket path too long");
close_socket();
return 0;
}
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
DEBUG_LOG(LOGF_NtpSignd, "Could not connect to signd : %s", strerror(errno));
DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
close_socket();
return 0;
}
DEBUG_LOG(LOGF_NtpSignd, "Connected to signd");
DEBUG_LOG("Connected to signd");
return 1;
}
@@ -170,25 +170,25 @@ process_response(SignInstance *inst)
double delay;
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid response ID");
DEBUG_LOG("Invalid response ID");
return;
}
if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
DEBUG_LOG(LOGF_NtpSignd, "Signing failed");
DEBUG_LOG("Signing failed");
return;
}
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid NTP socket");
DEBUG_LOG("Invalid NTP socket");
return;
}
SCH_GetLastEventTime(NULL, NULL, &ts);
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
DEBUG_LOG(LOGF_NtpSignd, "Signing succeeded (delay %f)", delay);
DEBUG_LOG("Signing succeeded (delay %f)", delay);
/* Send the signed NTP packet */
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
@@ -222,12 +222,12 @@ read_write_socket(int sock_fd, int event, void *anything)
inst->request_length - inst->sent, 0);
if (s < 0) {
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
DEBUG_LOG("signd socket error: %s", strerror(errno));
close_socket();
return;
}
DEBUG_LOG(LOGF_NtpSignd, "Sent %d bytes to signd", s);
DEBUG_LOG("Sent %d bytes to signd", s);
inst->sent += s;
/* Try again later if the request is not complete yet */
@@ -235,12 +235,12 @@ read_write_socket(int sock_fd, int event, void *anything)
return;
/* Disable output and wait for a response */
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 0);
}
if (event == SCH_FILE_INPUT) {
if (IS_QUEUE_EMPTY()) {
DEBUG_LOG(LOGF_NtpSignd, "Unexpected signd response");
DEBUG_LOG("Unexpected signd response");
close_socket();
return;
}
@@ -251,15 +251,15 @@ read_write_socket(int sock_fd, int event, void *anything)
if (s <= 0) {
if (s < 0)
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
DEBUG_LOG("signd socket error: %s", strerror(errno));
else
DEBUG_LOG(LOGF_NtpSignd, "signd socket closed");
DEBUG_LOG("signd socket closed");
close_socket();
return;
}
DEBUG_LOG(LOGF_NtpSignd, "Received %d bytes from signd", s);
DEBUG_LOG("Received %d bytes from signd", s);
inst->received += s;
if (inst->received < sizeof (inst->response.length))
@@ -269,7 +269,7 @@ read_write_socket(int sock_fd, int event, void *anything)
if (response_length < offsetof(SigndResponse, signed_packet) ||
response_length > sizeof (SigndResponse)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid response length");
DEBUG_LOG("Invalid response length");
close_socket();
return;
}
@@ -283,7 +283,7 @@ read_write_socket(int sock_fd, int event, void *anything)
/* Move the head and enable output for the next packet */
queue_head = NEXT_QUEUE_INDEX(queue_head);
if (!IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
}
}
@@ -303,7 +303,7 @@ NSD_Initialise()
ARR_SetSize(queue, MAX_QUEUE_LENGTH);
queue_head = queue_tail = 0;
LOG(LOGS_INFO, LOGF_NtpSignd, "MS-SNTP authentication enabled");
LOG(LOGS_INFO, "MS-SNTP authentication enabled");
}
/* ================================================== */
@@ -333,17 +333,17 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
SignInstance *inst;
if (!enabled) {
DEBUG_LOG(LOGF_NtpSignd, "signd disabled");
DEBUG_LOG("signd disabled");
return 0;
}
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
DEBUG_LOG(LOGF_NtpSignd, "signd queue full");
DEBUG_LOG("signd queue full");
return 0;
}
if (length != NTP_NORMAL_PACKET_LENGTH) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid packet length");
DEBUG_LOG("Invalid packet length");
return 0;
}
@@ -369,12 +369,11 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
DEBUG_LOG(LOGF_NtpSignd, "Packet added to signd queue (%u:%u)",
queue_head, queue_tail);
DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
return 1;
}

View File

@@ -39,6 +39,7 @@
#include "local.h"
#include "memory.h"
#include "nameserv_async.h"
#include "privops.h"
#include "sched.h"
/* ================================================== */
@@ -205,12 +206,13 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
unsigned short port;
size = ARR_GetSize(records);
*slot = 0;
*found = 0;
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6) {
*found = *slot = 0;
remote_addr->ip_addr.family != IPADDR_INET6)
return;
}
hash = UTI_IPToHash(&remote_addr->ip_addr);
port = remote_addr->port;
@@ -229,8 +231,6 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
return;
}
}
*found = 0;
}
/* ================================================== */
@@ -354,7 +354,7 @@ replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
/* The hash table must be rebuilt for the new address */
rehash_records();
LOG(LOGS_INFO, LOGF_NtpSources, "Source %s replaced with %s",
LOG(LOGS_INFO, "Source %s replaced with %s",
UTI_IPToString(&old_addr->ip_addr),
UTI_IPToString(&new_addr->ip_addr));
@@ -377,7 +377,7 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
address.port = us->port;
DEBUG_LOG(LOGF_NtpSources, "(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
if (us->replacement) {
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
@@ -404,7 +404,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
assert(us == resolving_source);
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %d addrs", us->name, n_addrs);
DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs);
switch (status) {
case DNS_TryAgain:
@@ -413,7 +413,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
process_resolved_name(us, ip_addrs, n_addrs);
break;
case DNS_Failure:
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name);
LOG(LOGS_WARN, "Invalid host %s", us->name);
break;
default:
assert(0);
@@ -438,7 +438,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
if (next) {
/* Continue with the next source in the list */
DEBUG_LOG(LOGF_NtpSources, "resolving %s", next->name);
DEBUG_LOG("resolving %s", next->name);
DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
} else {
/* This was the last source in the list. If some sources couldn't
@@ -469,14 +469,14 @@ resolve_sources(void *arg)
assert(!resolving_source);
DNS_Reload();
PRV_ReloadDNS();
/* Start with the first source in the list, name_resolve_handler
will iterate over the rest */
us = unresolved_sources;
resolving_source = us;
DEBUG_LOG(LOGF_NtpSources, "resolving %s", us->name);
DEBUG_LOG("resolving %s", us->name);
DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
}
@@ -657,8 +657,7 @@ resolve_source_replacement(SourceRecord *record)
{
struct UnresolvedSource *us;
DEBUG_LOG(LOGF_NtpSources, "trying to replace %s",
UTI_IPToString(&record->remote_addr->ip_addr));
DEBUG_LOG("trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr));
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
@@ -704,7 +703,7 @@ NSR_HandleBadSource(IPAddr *address)
SCH_GetLastEventTime(NULL, NULL, &now);
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG(LOGF_NtpSources, "replacement postponed");
DEBUG_LOG("replacement postponed");
return;
}
last_replacement = now;
@@ -742,7 +741,7 @@ static void remove_tentative_pool_sources(int pool)
if (!record->remote_addr || record->pool != pool || !record->tentative)
continue;
DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s",
DEBUG_LOG("removing tentative source %s",
UTI_IPToString(&record->remote_addr->ip_addr));
clean_source_record(record);
@@ -800,8 +799,7 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
pool = ARR_GetElement(pools, record->pool);
pool->sources++;
DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources",
record->name, pool->sources);
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources);
/* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */
@@ -862,48 +860,14 @@ slew_sources(struct timespec *raw,
/* ================================================== */
int
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
{
SourceRecord *record;
unsigned int i;
int any;
NSR_ResolveSources();
any = 0;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr) {
if (address->family == IPADDR_UNSPEC ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
any = 1;
NCR_TakeSourceOnline(record->data);
}
}
}
if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us;
for (us = unresolved_sources; us; us = us->next) {
if (us->replacement)
continue;
any = 1;
us->new_source.params.online = 1;
}
}
return any;
}
/* ================================================== */
int
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
SourceRecord *record, *syncpeer;
unsigned int i, any;
if (connectivity != SRC_OFFLINE)
NSR_ResolveSources();
any = 0;
syncpeer = NULL;
for (i = 0; i < ARR_GetSize(records); i++) {
@@ -916,15 +880,14 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
syncpeer = record;
continue;
}
NCR_TakeSourceOffline(record->data);
NCR_SetConnectivity(record->data, connectivity);
}
}
}
/* Take sync peer offline as last to avoid reference switching */
if (syncpeer) {
NCR_TakeSourceOffline(syncpeer->data);
}
/* Set the sync peer last to avoid unnecessary reference switching */
if (syncpeer)
NCR_SetConnectivity(syncpeer->data, connectivity);
if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us;
@@ -933,7 +896,7 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
if (us->replacement)
continue;
any = 1;
us->new_source.params.online = 0;
us->new_source.params.connectivity = connectivity;
}
}

View File

@@ -102,14 +102,9 @@ extern void NSR_Initialise(void);
extern void NSR_Finalise(void);
/* This routine is used to indicate that sources whose IP addresses
match a particular subnet should be set online again. Returns a
flag indicating whether any hosts matched the address */
extern int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address);
/* This routine is used to indicate that sources whose IP addresses
match a particular subnet should be set offline. Returns a flag
indicating whether any hosts matched the address */
extern int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address);
match a particular subnet should be set online or offline. It returns
a flag indicating whether any hosts matched the address. */
extern int NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity);
extern int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll);

View File

@@ -114,8 +114,12 @@ static const struct request_length request_lengths[] = {
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER2 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER2 */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */
};
static const uint16_t reply_lengths[] = {
@@ -123,19 +127,21 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(null), /* NULL */
RPY_LENGTH_ENTRY(n_sources), /* N_SOURCES */
RPY_LENGTH_ENTRY(source_data), /* SOURCE_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP */
0, /* MANUAL_TIMESTAMP */
RPY_LENGTH_ENTRY(tracking), /* TRACKING */
RPY_LENGTH_ENTRY(sourcestats), /* SOURCESTATS */
RPY_LENGTH_ENTRY(rtc), /* RTC */
0, /* SUBNETS_ACCESSED - not supported */
0, /* CLIENT_ACCESSES - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX - not supported */
0, /* MANUAL_LIST - variable length */
0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
};
/* ================================================== */
@@ -192,21 +198,6 @@ PKL_ReplyLength(CMD_Reply *r)
if (type < 1 || type >= N_REPLY_TYPES)
return 0;
/* Length of MANUAL_LIST depends on number of samples stored in it */
if (type == RPY_MANUAL_LIST) {
uint32_t ns;
if (r->status != htons(STT_SUCCESS))
return offsetof(CMD_Reply, data);
ns = ntohl(r->data.manual_list.n_samples);
if (ns > MAX_MANUAL_LIST_SAMPLES)
return 0;
return offsetof(CMD_Reply, data.manual_list.samples) +
ns * sizeof (RPY_ManualListSample);
}
return reply_lengths[type];
}

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Bryan Christianson 2015
* Copyright (C) Miroslav Lichvar 2016
* Copyright (C) Miroslav Lichvar 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -40,6 +40,7 @@
#define OP_SETTIME 1026
#define OP_BINDSOCKET 1027
#define OP_NAME2IPADDRESS 1028
#define OP_RELOADDNS 1029
#define OP_QUIT 1099
union sockaddr_in46 {
@@ -267,7 +268,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
sock_fd = req->sock;
UTI_SockaddrToIPAndPort(sa, &ip, &port);
if (port && port != CNF_GetNTPPort()) {
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) {
close(sock_fd);
res_fatal(res, "Invalid port %d", port);
return;
@@ -293,8 +294,6 @@ do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
/* make sure the string is terminated */
req->name[sizeof (req->name) - 1] = '\0';
DNS_Reload();
res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses,
DNS_MAX_ADDRESSES);
}
@@ -302,6 +301,19 @@ do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
/* ======================================================================= */
/* HELPER - perform DNS_Reload() */
#ifdef PRIVOPS_RELOADDNS
static void
do_reload_dns(PrvResponse *res)
{
DNS_Reload();
res->rc = 0;
}
#endif
/* ======================================================================= */
/* HELPER - main loop - action requests from the daemon */
static void
@@ -343,6 +355,11 @@ helper_main(int fd)
case OP_NAME2IPADDRESS:
do_name_to_ipaddress(&req.data.name_to_ipaddress, &res);
break;
#endif
#ifdef PRIVOPS_RELOADDNS
case OP_RELOADDNS:
do_reload_dns(&res);
break;
#endif
case OP_QUIT:
quit = 1;
@@ -371,14 +388,14 @@ receive_response(PrvResponse *res)
resp_len = recv(helper_fd, res, sizeof (*res), 0);
if (resp_len < 0)
LOG_FATAL(LOGF_PrivOps, "Could not read from helper : %s", strerror(errno));
LOG_FATAL("Could not read from helper : %s", strerror(errno));
if (resp_len != sizeof (*res))
LOG_FATAL(LOGF_PrivOps, "Invalid helper response");
LOG_FATAL("Invalid helper response");
if (res->fatal_error)
LOG_FATAL(LOGF_PrivOps, "Error in helper : %s", res->data.fatal_msg.msg);
LOG_FATAL("Error in helper : %s", res->data.fatal_msg.msg);
DEBUG_LOG(LOGF_PrivOps, "Received response rc=%d", res->rc);
DEBUG_LOG("Received response rc=%d", res->rc);
/* if operation failed in the helper, set errno so daemon can print log message */
if (res->res_errno)
@@ -429,10 +446,10 @@ send_request(PrvRequest *req)
if (sendmsg(helper_fd, &msg, 0) < 0) {
/* don't try to send another request from exit() */
helper_fd = -1;
LOG_FATAL(LOGF_PrivOps, "Could not send to helper : %s", strerror(errno));
LOG_FATAL("Could not send to helper : %s", strerror(errno));
}
DEBUG_LOG(LOGF_PrivOps, "Sent request op=%d", req->op);
DEBUG_LOG("Sent request op=%d", req->op);
}
/* ======================================================================= */
@@ -562,7 +579,8 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
unsigned short port;
UTI_SockaddrToIPAndPort(address, &ip, &port);
assert(!port || port == CNF_GetNTPPort());
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort())
assert(0);
if (!have_helper())
return bind(sock, address, address_len);
@@ -598,7 +616,7 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
req.op = OP_NAME2IPADDRESS;
if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
"%s", name) >= sizeof (req.data.name_to_ipaddress.name)) {
DEBUG_LOG(LOGF_PrivOps, "Name too long");
DEBUG_LOG("Name too long");
return DNS_Failure;
}
@@ -613,6 +631,30 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
/* ======================================================================= */
/* DAEMON - request res_init() */
#ifdef PRIVOPS_RELOADDNS
void
PRV_ReloadDNS(void)
{
PrvRequest req;
PrvResponse res;
if (!have_helper()) {
DNS_Reload();
return;
}
memset(&req, 0, sizeof (req));
req.op = OP_RELOADDNS;
submit_request(&req, &res);
assert(!res.rc);
}
#endif
/* ======================================================================= */
void
PRV_Initialise(void)
{
@@ -631,21 +673,21 @@ PRV_StartHelper(void)
int fd, sock_pair[2];
if (have_helper())
LOG_FATAL(LOGF_PrivOps, "Helper already running");
LOG_FATAL("Helper already running");
if (
#ifdef SOCK_SEQPACKET
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) &&
#endif
socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair))
LOG_FATAL(LOGF_PrivOps, "socketpair() failed : %s", strerror(errno));
LOG_FATAL("socketpair() failed : %s", strerror(errno));
UTI_FdSetCloexec(sock_pair[0]);
UTI_FdSetCloexec(sock_pair[1]);
pid = fork();
if (pid < 0)
LOG_FATAL(LOGF_PrivOps, "fork() failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid == 0) {
/* child process */
@@ -658,7 +700,7 @@ PRV_StartHelper(void)
}
/* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN);
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
helper_main(sock_pair[1]);

View File

@@ -58,6 +58,12 @@ int PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs);
#define PRV_Name2IPAddress DNS_Name2IPAddress
#endif
#ifdef PRIVOPS_RELOADDNS
void PRV_ReloadDNS(void);
#else
#define PRV_ReloadDNS DNS_Reload
#endif
#ifdef PRIVOPS_HELPER
void PRV_Initialise(void);
void PRV_StartHelper(void);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -37,6 +37,7 @@
#include "sources.h"
#include "logging.h"
#include "regress.h"
#include "samplefilt.h"
#include "sched.h"
/* list of refclock drivers */
@@ -75,15 +76,19 @@ struct RCL_Instance_Record {
int driver_polled;
int poll;
int leap_status;
int pps_forced;
int pps_rate;
int pps_active;
int max_lock_age;
struct MedianFilter filter;
int stratum;
int tai;
uint32_t ref_id;
uint32_t lock_ref;
double offset;
double delay;
double precision;
double pulse_width;
SPF_Instance filter;
SCH_TimeoutID timeout_id;
SRC_Instance source;
};
@@ -93,7 +98,7 @@ static ARR_Instance refclocks;
static LOG_FileID logfileid;
static int valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked);
static int valid_sample_time(RCL_Instance instance, struct timespec *sample_time);
static int pps_stratum(RCL_Instance instance, struct timespec *ts);
static void poll_timeout(void *arg);
static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
@@ -101,18 +106,6 @@ static void slew_samples(struct timespec *raw, struct timespec *cooked, double d
static void add_dispersion(double dispersion, void *anything);
static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
static void filter_fini(struct MedianFilter *filter);
static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion);
static int filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
static int filter_get_samples(struct MedianFilter *filter);
static int filter_select_samples(struct MedianFilter *filter);
static int filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset);
static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
static RCL_Instance
get_refclock(unsigned int index)
{
@@ -147,7 +140,7 @@ RCL_Finalise(void)
if (inst->driver->fini)
inst->driver->fini(inst);
filter_fini(&inst->filter);
SPF_DestroyInstance(inst->filter);
Free(inst->driver_parameter);
SRC_DestroyInstance(inst->source);
Free(inst);
@@ -164,7 +157,6 @@ RCL_Finalise(void)
int
RCL_AddRefclock(RefclockParameters *params)
{
int pps_source = 0;
RCL_Instance inst;
inst = MallocNew(struct RCL_Instance_Record);
@@ -172,27 +164,21 @@ RCL_AddRefclock(RefclockParameters *params)
if (strcmp(params->driver_name, "SHM") == 0) {
inst->driver = &RCL_SHM_driver;
inst->precision = 1e-6;
} else if (strcmp(params->driver_name, "SOCK") == 0) {
inst->driver = &RCL_SOCK_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PPS") == 0) {
inst->driver = &RCL_PPS_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PHC") == 0) {
inst->driver = &RCL_PHC_driver;
inst->precision = 1e-9;
} else {
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
return 0;
LOG_FATAL("unknown refclock driver %s", params->driver_name);
}
if (!inst->driver->init && !inst->driver->poll) {
LOG_FATAL(LOGF_Refclock, "refclock driver %s is not compiled in", params->driver_name);
return 0;
}
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");
inst->data = NULL;
inst->driver_parameter = params->driver_parameter;
@@ -201,14 +187,18 @@ RCL_AddRefclock(RefclockParameters *params)
inst->poll = params->poll;
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age;
inst->stratum = params->stratum;
inst->tai = params->tai;
inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset;
inst->delay = params->delay;
if (params->precision > 0.0)
inst->precision = params->precision;
inst->precision = LCL_GetSysPrecisionAsQuantum();
inst->precision = MAX(inst->precision, params->precision);
inst->pulse_width = params->pulse_width;
inst->timeout_id = -1;
inst->source = NULL;
@@ -221,12 +211,8 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_parameter[i] = '\0';
}
if (pps_source) {
if (inst->pps_rate < 1)
inst->pps_rate = 1;
} else {
inst->pps_rate = 0;
}
if (inst->pps_rate < 1)
inst->pps_rate = 1;
if (params->ref_id)
inst->ref_id = params->ref_id;
@@ -251,25 +237,26 @@ RCL_AddRefclock(RefclockParameters *params)
max_samples = 1 << (inst->poll - inst->driver_poll);
if (max_samples < params->filter_length) {
if (max_samples < 4) {
LOG(LOGS_WARN, LOGF_Refclock, "Setting filter length for %s to %d",
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)
if (!inst->driver->init(inst)) {
LOG_FATAL(LOGF_Refclock, "refclock %s initialisation failed", params->driver_name);
return 0;
}
if (inst->driver->init && !inst->driver->init(inst))
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
/* 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);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples);
params->min_samples, params->max_samples, 0.0, 0.0);
DEBUG_LOG(LOGF_Refclock, "refclock %s refid=%s poll=%d dpoll=%d filter=%d",
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id),
inst->poll, inst->driver_poll, params->filter_length);
@@ -338,44 +325,123 @@ RCL_GetDriverParameter(RCL_Instance instance)
return instance->driver_parameter;
}
static char *
get_next_driver_option(RCL_Instance instance, char *option)
{
if (option == NULL)
option = instance->driver_parameter;
option += strlen(option) + 1;
if (option >= instance->driver_parameter + instance->driver_parameter_length)
return NULL;
return option;
}
void
RCL_CheckDriverOptions(RCL_Instance instance, const char **options)
{
char *option;
int i, len;
for (option = get_next_driver_option(instance, NULL);
option;
option = get_next_driver_option(instance, option)) {
for (i = 0; options && options[i]; i++) {
len = strlen(options[i]);
if (!strncmp(options[i], option, len) &&
(option[len] == '=' || option[len] == '\0'))
break;
}
if (!options || !options[i])
LOG_FATAL("Invalid refclock driver option %s", option);
}
}
char *
RCL_GetDriverOption(RCL_Instance instance, char *name)
{
char *s, *e;
int n;
char *option;
int len;
s = instance->driver_parameter;
e = s + instance->driver_parameter_length;
n = strlen(name);
len = strlen(name);
while (1) {
s += strlen(s) + 1;
if (s >= e)
break;
if (!strncmp(name, s, n)) {
if (s[n] == '=')
return s + n + 1;
if (s[n] == '\0')
return s + n;
for (option = get_next_driver_option(instance, NULL);
option;
option = get_next_driver_option(instance, option)) {
if (!strncmp(name, option, len)) {
if (option[len] == '=')
return option + len + 1;
if (option[len] == '\0')
return option + len;
}
}
return NULL;
}
static int
convert_tai_offset(struct timespec *sample_time, double *offset)
{
struct timespec tai_ts, utc_ts;
int tai_offset;
/* Get approximate TAI-UTC offset for the reference time in TAI */
UTI_AddDoubleToTimespec(sample_time, *offset, &tai_ts);
tai_offset = REF_GetTaiOffset(&tai_ts);
/* Get TAI-UTC offset for the reference time in UTC +/- 1 second */
UTI_AddDoubleToTimespec(&tai_ts, -tai_offset, &utc_ts);
tai_offset = REF_GetTaiOffset(&utc_ts);
if (!tai_offset)
return 0;
*offset -= tai_offset;
return 1;
}
static int
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
{
NTP_Sample sample;
sample.time = *sample_time;
sample.offset = offset;
sample.peer_delay = instance->delay;
sample.root_delay = instance->delay;
sample.peer_dispersion = dispersion;
sample.root_dispersion = dispersion;
sample.leap = instance->leap_status;
/* Handle special case when PPS is used with the local reference */
if (instance->pps_active && instance->lock_ref == -1)
sample.stratum = pps_stratum(instance, &sample.time);
else
sample.stratum = instance->stratum;
return SPF_AccumulateSample(instance->filter, &sample);
}
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
double correction, dispersion;
struct timespec cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time, -offset);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time, &cooked_time))
!valid_sample_time(instance, &cooked_time))
return 0;
switch (leap) {
@@ -385,11 +451,19 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
instance->leap_status = leap;
break;
default:
DEBUG_LOG(LOGF_Refclock, "refclock sample ignored bad leap %d", leap);
DEBUG_LOG("refclock sample ignored bad leap %d", leap);
return 0;
}
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
DEBUG_LOG("refclock sample ignored unknown TAI offset");
return 0;
}
if (!accumulate_sample(instance, &cooked_time,
offset - correction + instance->offset, dispersion))
return 0;
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
@@ -404,24 +478,57 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
int
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
{
double correction, dispersion, offset;
double correction, dispersion;
struct timespec cooked_time;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
second += correction;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
return 0;
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
}
static int
check_pulse_edge(RCL_Instance instance, double offset, double distance)
{
double max_error;
if (instance->pulse_width <= 0.0)
return 1;
max_error = 1.0 / instance->pps_rate - instance->pulse_width;
max_error = MIN(instance->pulse_width, max_error);
max_error *= 0.5;
if (fabs(offset) > max_error || distance > max_error) {
DEBUG_LOG("refclock pulse ignored offset=%.9f distance=%.9f max_error=%.9f",
offset, distance, max_error);
return 0;
}
return 1;
}
int
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction)
{
double offset;
int rate;
NTP_Leap leap;
leap = LEAP_Normal;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
!valid_sample_time(instance, pulse_time, &cooked_time))
if (!UTI_IsTimeOffsetSane(cooked_time, second) ||
!valid_sample_time(instance, cooked_time))
return 0;
leap = LEAP_Normal;
dispersion += instance->precision;
rate = instance->pps_rate;
assert(rate > 0);
offset = -second - correction + instance->offset;
offset = -second + instance->offset;
/* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
offset -= (long)(offset * rate) / (double)rate;
@@ -432,44 +539,47 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
if (instance->lock_ref != -1) {
RCL_Instance lock_refclock;
struct timespec ref_sample_time;
double sample_diff, ref_offset, ref_dispersion, shift;
NTP_Sample ref_sample;
double sample_diff, shift;
lock_refclock = get_refclock(instance->lock_ref);
if (!filter_get_last_sample(&lock_refclock->filter,
&ref_sample_time, &ref_offset, &ref_dispersion)) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample");
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
sample_diff = UTI_DiffTimespecsToDouble(&cooked_time, &ref_sample_time);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff);
return 0;
}
/* Align the offset to the reference sample */
if ((ref_offset - offset) >= 0.0)
shift = (long)((ref_offset - offset) * rate + 0.5) / (double)rate;
if ((ref_sample.offset - offset) >= 0.0)
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_offset - offset) * rate - 0.5) / (double)rate;
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
offset += shift;
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_offset - offset, ref_dispersion, dispersion);
if (fabs(ref_sample.offset - offset) +
ref_sample.root_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
return 0;
}
if (!check_pulse_edge(instance, ref_sample.offset - offset, 0.0))
return 0;
leap = lock_refclock->leap_status;
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff);
DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
offset, ref_sample.offset - offset, sample_diff);
} else {
struct timespec ref_time;
int is_synchronised, stratum;
@@ -479,24 +589,30 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
/* Ignore the pulse if we are not well synchronized and the local
reference is not active */
REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum,
REF_GetReferenceParams(cooked_time, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
distance = fabs(root_delay) / 2 + root_dispersion;
if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
second, leap != LEAP_Unsynchronised, distance);
DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
offset, leap != LEAP_Unsynchronised, distance);
/* Drop also all stored samples */
filter_reset(&instance->filter);
SPF_DropSamples(instance->filter);
return 0;
}
if (!check_pulse_edge(instance, offset, distance))
return 0;
}
filter_add_sample(&instance->filter, &cooked_time, offset, dispersion);
if (!accumulate_sample(instance, cooked_time, offset, dispersion))
return 0;
instance->leap_status = leap;
instance->pps_active = 1;
log_sample(instance, &cooked_time, 0, 1, offset + correction - instance->offset, offset, dispersion);
log_sample(instance, cooked_time, 0, 1, offset + raw_correction - instance->offset,
offset, dispersion);
/* for logging purposes */
if (!instance->driver->poll)
@@ -505,23 +621,31 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
return 1;
}
static int
valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked)
double
RCL_GetPrecision(RCL_Instance instance)
{
struct timespec now_raw, last_sample_time;
double diff, last_offset, last_dispersion;
return instance->precision;
}
LCL_ReadRawTime(&now_raw);
diff = UTI_DiffTimespecsToDouble(&now_raw, raw);
int
RCL_GetDriverPoll(RCL_Instance instance)
{
return instance->driver_poll;
}
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
(filter_get_samples(&instance->filter) > 0 &&
filter_get_last_sample(&instance->filter, &last_sample_time,
&last_offset, &last_dispersion) &&
UTI_CompareTimespecs(&last_sample_time, cooked) >= 0)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f raw=%s cooked=%s",
UTI_RefidToString(instance->ref_id), diff,
UTI_TimespecToString(raw), UTI_TimespecToString(cooked));
static int
valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
{
struct timespec now;
double diff;
LCL_ReadCookedTime(&now, NULL);
diff = UTI_DiffTimespecsToDouble(&now, sample_time);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) {
DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
UTI_RefidToString(instance->ref_id),
UTI_TimespecToString(sample_time), diff);
return 0;
}
@@ -562,6 +686,7 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
static void
poll_timeout(void *arg)
{
NTP_Sample sample;
int poll;
RCL_Instance inst = (RCL_Instance)arg;
@@ -575,26 +700,14 @@ poll_timeout(void *arg)
}
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion;
struct timespec sample_time;
int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
inst->driver_polled = 0;
if (sample_ok) {
if (inst->pps_active && inst->lock_ref == -1)
/* Handle special case when PPS is used with local stratum */
stratum = pps_stratum(inst, &sample_time);
else
stratum = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) {
SRC_UpdateReachability(inst->source, 1);
SRC_AccumulateSample(inst->source, &sample_time, offset,
inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);
log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion);
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);
}
@@ -611,9 +724,9 @@ slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
for (i = 0; i < ARR_GetSize(refclocks); i++) {
if (change_type == LCL_ChangeUnknownStep)
filter_reset(&get_refclock(i)->filter);
SPF_DropSamples(get_refclock(i)->filter);
else
filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset);
SPF_SlewSamples(get_refclock(i)->filter, cooked, dfreq, doffset);
}
}
@@ -623,7 +736,7 @@ add_dispersion(double dispersion, void *anything)
unsigned int i;
for (i = 0; i < ARR_GetSize(refclocks); i++)
filter_add_dispersion(&get_refclock(i)->filter, dispersion);
SPF_AddDispersion(get_refclock(i)->filter, dispersion);
}
static void
@@ -655,320 +768,3 @@ log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, in
dispersion);
}
}
static void
filter_init(struct MedianFilter *filter, int length, double max_dispersion)
{
if (length < 1)
length = 1;
filter->length = length;
filter->index = -1;
filter->used = 0;
filter->last = -1;
/* set first estimate to system precision */
filter->avg_var_n = 0;
filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
filter->max_var = max_dispersion * max_dispersion;
filter->samples = MallocArray(struct FilterSample, filter->length);
filter->selected = MallocArray(int, filter->length);
filter->x_data = MallocArray(double, filter->length);
filter->y_data = MallocArray(double, filter->length);
filter->w_data = MallocArray(double, filter->length);
}
static void
filter_fini(struct MedianFilter *filter)
{
Free(filter->samples);
Free(filter->selected);
Free(filter->x_data);
Free(filter->y_data);
Free(filter->w_data);
}
static void
filter_reset(struct MedianFilter *filter)
{
filter->index = -1;
filter->used = 0;
}
static double
filter_get_avg_sample_dispersion(struct MedianFilter *filter)
{
return sqrt(filter->avg_var);
}
static void
filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
{
filter->index++;
filter->index %= filter->length;
filter->last = filter->index;
if (filter->used < filter->length)
filter->used++;
filter->samples[filter->index].sample_time = *sample_time;
filter->samples[filter->index].offset = offset;
filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
}
static int
filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
if (filter->last < 0)
return 0;
*sample_time = filter->samples[filter->last].sample_time;
*offset = filter->samples[filter->last].offset;
*dispersion = filter->samples[filter->last].dispersion;
return 1;
}
static int
filter_get_samples(struct MedianFilter *filter)
{
return filter->used;
}
static const struct FilterSample *tmp_sorted_array;
static int
sample_compare(const void *a, const void *b)
{
const struct FilterSample *s1, *s2;
s1 = &tmp_sorted_array[*(int *)a];
s2 = &tmp_sorted_array[*(int *)b];
if (s1->offset < s2->offset)
return -1;
else if (s1->offset > s2->offset)
return 1;
return 0;
}
int
filter_select_samples(struct MedianFilter *filter)
{
int i, j, k, o, from, to, *selected;
double min_dispersion;
if (filter->used < 1)
return 0;
/* for lengths below 4 require full filter,
for 4 and above require at least 4 samples */
if ((filter->length < 4 && filter->used != filter->length) ||
(filter->length >= 4 && filter->used < 4))
return 0;
selected = filter->selected;
if (filter->used > 4) {
/* select samples with dispersion better than 1.5 * minimum */
for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) {
if (min_dispersion > filter->samples[i].dispersion)
min_dispersion = filter->samples[i].dispersion;
}
for (i = j = 0; i < filter->used; i++) {
if (filter->samples[i].dispersion <= 1.5 * min_dispersion)
selected[j++] = i;
}
} else {
j = 0;
}
if (j < 4) {
/* select all samples */
for (j = 0; j < filter->used; j++)
selected[j] = j;
}
/* and sort their indices by offset */
tmp_sorted_array = filter->samples;
qsort(selected, j, sizeof (int), sample_compare);
/* select 60 percent of the samples closest to the median */
if (j > 2) {
from = j / 5;
if (from < 1)
from = 1;
to = j - from;
} else {
from = 0;
to = j;
}
/* mark unused samples and sort the rest from oldest to newest */
o = filter->used - filter->index - 1;
for (i = 0; i < from; i++)
selected[i] = -1;
for (; i < to; i++)
selected[i] = (selected[i] + o) % filter->used;
for (; i < filter->used; i++)
selected[i] = -1;
for (i = from; i < to; i++) {
j = selected[i];
selected[i] = -1;
while (j != -1 && selected[j] != j) {
k = selected[j];
selected[j] = j;
j = k;
}
}
for (i = j = 0, k = -1; i < filter->used; i++) {
if (selected[i] != -1)
selected[j++] = (selected[i] + filter->used - o) % filter->used;
}
return j;
}
static int
filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
struct FilterSample *s, *ls;
int i, n, dof;
double x, y, d, e, var, prev_avg_var;
n = filter_select_samples(filter);
if (n < 1)
return 0;
ls = &filter->samples[filter->selected[n - 1]];
/* prepare data */
for (i = 0; i < n; i++) {
s = &filter->samples[filter->selected[i]];
filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
filter->y_data[i] = s->offset;
filter->w_data[i] = s->dispersion;
}
/* mean offset, sample time and sample dispersion */
for (i = 0, x = y = e = 0.0; i < n; i++) {
x += filter->x_data[i];
y += filter->y_data[i];
e += filter->w_data[i];
}
x /= n;
y /= n;
e /= n;
if (n >= 4) {
double b0, b1, s2, sb0, sb1;
/* set y axis to the mean sample time */
for (i = 0; i < n; i++)
filter->x_data[i] -= x;
/* make a linear fit and use the estimated standard deviation of intercept
as dispersion */
RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
&b0, &b1, &s2, &sb0, &sb1);
var = s2;
d = sb0;
dof = n - 2;
} else if (n >= 2) {
for (i = 0, d = 0.0; i < n; i++)
d += (filter->y_data[i] - y) * (filter->y_data[i] - y);
var = d / (n - 1);
d = sqrt(var);
dof = n - 1;
} else {
var = filter->avg_var;
d = sqrt(var);
dof = 1;
}
/* avoid having zero dispersion */
if (var < 1e-20) {
var = 1e-20;
d = sqrt(var);
}
/* drop the sample if variance is larger than allowed maximum */
if (filter->max_var > 0.0 && var > filter->max_var) {
DEBUG_LOG(LOGF_Refclock, "filter dispersion too large disp=%.9f max=%.9f",
sqrt(var), sqrt(filter->max_var));
return 0;
}
prev_avg_var = filter->avg_var;
/* update exponential moving average of the variance */
if (filter->avg_var_n > 50) {
filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
} else {
filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
(dof + filter->avg_var_n);
if (filter->avg_var_n == 0)
prev_avg_var = filter->avg_var;
filter->avg_var_n += dof;
}
/* reduce noise in sourcestats weights by using the long-term average
instead of the estimated variance if it's not significantly lower */
if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
d = sqrt(filter->avg_var) * d / sqrt(var);
if (d < e)
d = e;
UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
*offset = y;
*dispersion = d;
filter_reset(filter);
return 1;
}
static void
filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
{
int i, first, last;
double delta_time;
struct timespec *sample;
if (filter->last < 0)
return;
/* always slew the last sample as it may be needed by PPS refclocks */
if (filter->used > 0) {
first = 0;
last = filter->used - 1;
} else {
first = last = filter->last;
}
for (i = first; i <= last; i++) {
sample = &filter->samples[i].sample_time;
UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
filter->samples[i].offset -= delta_time;
}
}
static void
filter_add_dispersion(struct MedianFilter *filter, double dispersion)
{
int i;
for (i = 0; i < filter->used; i++) {
filter->samples[i].dispersion += dispersion;
}
}

View File

@@ -37,17 +37,21 @@ typedef struct {
int driver_poll;
int poll;
int filter_length;
int pps_forced;
int pps_rate;
int min_samples;
int max_samples;
int sel_options;
int max_lock_age;
int stratum;
int tai;
uint32_t ref_id;
uint32_t lock_ref_id;
double offset;
double delay;
double precision;
double max_dispersion;
double pulse_width;
} RefclockParameters;
typedef struct RCL_Instance_Record *RCL_Instance;
@@ -68,8 +72,13 @@ extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
extern int RCL_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 double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2013
* Copyright (C) Miroslav Lichvar 2013, 2017
*
* 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,144 +33,137 @@
#include "sysincl.h"
#include <linux/ptp_clock.h>
#include "refclock.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
#include "sched.h"
#include "sys_linux.h"
/* From linux/include/linux/posix-timers.h */
#define CPUCLOCK_MAX 3
#define CLOCKFD CPUCLOCK_MAX
#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
#define NUM_READINGS 10
static int no_sys_offset_ioctl = 0;
struct phc_reading {
struct timespec sys_ts1;
struct timespec phc_ts;;
struct timespec sys_ts2;
struct phc_instance {
int fd;
int mode;
int nocrossts;
int extpps;
int pin;
int channel;
HCL_Instance clock;
};
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
{
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
struct ptp_sys_offset sys_off;
int i;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = n;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
LOG(LOGS_ERR, LOGF_Refclock, "ioctl(PTP_SYS_OFFSET) failed : %s", strerror(errno));
return 0;
}
for (i = 0; i < n; i++) {
readings[i].sys_ts1.tv_sec = sys_off.ts[i * 2].sec;
readings[i].sys_ts1.tv_nsec = sys_off.ts[i * 2].nsec;
readings[i].phc_ts.tv_sec = sys_off.ts[i * 2 + 1].sec;
readings[i].phc_ts.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
readings[i].sys_ts2.tv_sec = sys_off.ts[i * 2 + 2].sec;
readings[i].sys_ts2.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
}
return 1;
#else
/* Not available */
return 0;
#endif
}
static int read_phc_user(struct phc_reading *readings, int phc_fd, int n)
{
clockid_t phc_id;
int i;
phc_id = FD_TO_CLOCKID(phc_fd);
for (i = 0; i < n; i++) {
if (clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts1) ||
clock_gettime(phc_id, &readings[i].phc_ts) ||
clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts2)) {
LOG(LOGS_ERR, LOGF_Refclock, "clock_gettime() failed : %s", strerror(errno));
return 0;
}
}
return 1;
}
static void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance)
{
struct ptp_clock_caps caps;
int phc_fd;
char *path;
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
struct phc_instance *phc;
int phc_fd, rising_edge;
char *path, *s;
RCL_CheckDriverOptions(instance, options);
path = RCL_GetDriverParameter(instance);
phc_fd = open(path, O_RDONLY);
phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
LOG_FATAL("Could not open PHC");
return 0;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno));
return 0;
phc = MallocNew(struct phc_instance);
phc->fd = phc_fd;
phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
if (phc->extpps) {
s = RCL_GetDriverOption(instance, "pin");
phc->pin = s ? atoi(s) : 0;
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
} else {
phc->pin = phc->channel = 0;
phc->clock = NULL;
}
UTI_FdSetCloexec(phc_fd);
RCL_SetDriverData(instance, (void *)(long)phc_fd);
RCL_SetDriverData(instance, phc);
return 1;
}
static void phc_finalise(RCL_Instance instance)
{
close((long)RCL_GetDriverData(instance));
struct phc_instance *phc;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
HCL_DestroyInstance(phc->clock);
}
close(phc->fd);
Free(phc);
}
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
double local_err;
int channel;
instance = anything;
phc = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return;
}
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
}
static int phc_poll(RCL_Instance instance)
{
struct phc_reading readings[NUM_READINGS];
double offset = 0.0, delay, best_delay = 0.0;
int i, phc_fd, best;
phc_fd = (long)RCL_GetDriverData(instance);
struct phc_instance *phc;
struct timespec phc_ts, sys_ts, local_ts;
double offset, phc_err, local_err;
if (!no_sys_offset_ioctl) {
if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) {
no_sys_offset_ioctl = 1;
return 0;
}
} else {
if (!read_phc_user(readings, phc_fd, NUM_READINGS))
return 0;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &phc_err))
return 0;
if (phc->extpps) {
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
return 0;
}
/* Find the fastest reading */
for (i = 0; i < NUM_READINGS; i++) {
delay = UTI_DiffTimespecsToDouble(&readings[i].sys_ts2, &readings[i].sys_ts1);
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
if (!i || best_delay > delay) {
best = i;
best_delay = delay;
}
}
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
offset = UTI_DiffTimespecsToDouble(&readings[best].phc_ts, &readings[best].sys_ts2) +
best_delay / 2.0;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
return RCL_AddSample(instance, &readings[best].sys_ts2, offset, LEAP_Normal);
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {

View File

@@ -48,48 +48,51 @@ struct pps_instance {
};
static int pps_initialise(RCL_Instance instance) {
const char *options[] = {"clear", NULL};
pps_handle_t handle;
pps_params_t params;
struct pps_instance *pps;
int fd, edge_clear, mode;
char *path;
RCL_CheckDriverOptions(instance, options);
path = RCL_GetDriverParameter(instance);
edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0;
fd = open(path, O_RDWR);
if (fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
LOG_FATAL("Could not open %s : %s", path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_create() failed on %s", path);
LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getcap(handle, &mode) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_getcap() failed on %s", path);
LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getparams(handle, &params) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_getparams() failed on %s", path);
LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
return 0;
}
if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT)) {
LOG_FATAL(LOGF_Refclock, "CAPTUREASSERT not supported on %s", path);
LOG_FATAL("CAPTUREASSERT not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR;
} else {
if (!(mode & PPS_CAPTURECLEAR)) {
LOG_FATAL(LOGF_Refclock, "CAPTURECLEAR not supported on %s", path);
LOG_FATAL("CAPTURECLEAR not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTURECLEAR;
@@ -97,7 +100,7 @@ static int pps_initialise(RCL_Instance instance) {
}
if (time_pps_setparams(handle, &params) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_setparams() failed on %s", path);
LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
return 0;
}
@@ -133,7 +136,7 @@ static int pps_poll(RCL_Instance instance)
ts.tv_nsec = 0;
if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) {
LOG(LOGS_ERR, LOGF_Refclock, "time_pps_fetch() failed : %s", strerror(errno));
LOG(LOGS_ERR, "time_pps_fetch() failed : %s", strerror(errno));
return 0;
}
@@ -146,8 +149,8 @@ static int pps_poll(RCL_Instance instance)
}
if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%s",
seq, UTI_TimespecToString(&ts));
DEBUG_LOG("PPS sample ignored seq=%lu ts=%s",
(unsigned long)seq, UTI_TimespecToString(&ts));
return 0;
}

View File

@@ -59,23 +59,26 @@ struct shmTime {
};
static int shm_initialise(RCL_Instance instance) {
const char *options[] = {"perm", NULL};
int id, param, perm;
char *s;
struct shmTime *shm;
RCL_CheckDriverOptions(instance, options);
param = atoi(RCL_GetDriverParameter(instance));
s = RCL_GetDriverOption(instance, "perm");
perm = s ? strtol(s, NULL, 8) & 0777 : 0600;
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
if (id == -1) {
LOG_FATAL(LOGF_Refclock, "shmget() failed");
LOG_FATAL("shmget() failed : %s", strerror(errno));
return 0;
}
shm = (struct shmTime *)shmat(id, 0, 0);
if ((long)shm == -1) {
LOG_FATAL(LOGF_Refclock, "shmat() failed");
LOG_FATAL("shmat() failed : %s", strerror(errno));
return 0;
}
@@ -100,7 +103,7 @@ static int shm_poll(RCL_Instance instance)
if ((t.mode == 1 && t.count != shm->count) ||
!(t.mode == 0 || t.mode == 1) || !t.valid) {
DEBUG_LOG(LOGF_Refclock, "SHM sample ignored mode=%d count=%d valid=%d",
DEBUG_LOG("SHM sample ignored mode=%d count=%d valid=%d",
t.mode, t.count, t.valid);
return 0;
}

View File

@@ -69,20 +69,19 @@ static void read_sample(int sockfd, int event, void *anything)
s = recv(sockfd, &sample, sizeof (sample), 0);
if (s < 0) {
LOG(LOGS_ERR, LOGF_Refclock, "Could not read SOCK sample : %s",
strerror(errno));
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
return;
}
if (s != sizeof (sample)) {
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample));
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample));
return;
}
if (sample.magic != SOCK_MAGIC) {
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x",
sample.magic, SOCK_MAGIC);
DEBUG_LOG("Unexpected magic number in SOCK sample : %x != %x",
(unsigned int)sample.magic, (unsigned int)SOCK_MAGIC);
return;
}
@@ -102,17 +101,19 @@ static int sock_initialise(RCL_Instance instance)
int sockfd;
char *path;
RCL_CheckDriverOptions(instance, NULL);
path = RCL_GetDriverParameter(instance);
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
LOG_FATAL(LOGF_Refclock, "path %s is too long", path);
LOG_FATAL("Path %s too long", path);
return 0;
}
sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0) {
LOG_FATAL(LOGF_Refclock, "socket() failed");
LOG_FATAL("socket() failed");
return 0;
}
@@ -120,7 +121,7 @@ static int sock_initialise(RCL_Instance instance)
unlink(path);
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
LOG_FATAL(LOGF_Refclock, "bind() failed");
LOG_FATAL("bind(%s) failed : %s", path, strerror(errno));
return 0;
}

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016
* Copyright (C) Miroslav Lichvar 2009-2018
*
* 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
@@ -49,6 +49,7 @@ static int local_orphan;
static double local_distance;
static NTP_Leap our_leap_status;
static int our_leap_sec;
static int our_tai_offset;
static int our_stratum;
static uint32_t our_ref_id;
static IPAddr our_ref_ip;
@@ -111,8 +112,6 @@ static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname;
static time_t last_tz_leap_check;
static NTP_Leap tz_leap;
/* ================================================== */
@@ -141,7 +140,7 @@ static double last_ref_update_interval;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when);
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);
/* ================================================== */
@@ -157,7 +156,8 @@ handle_slew(struct timespec *raw,
double delta;
struct timespec now;
UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (!UTI_IsZeroTimespec(&our_ref_time))
UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
UTI_ZeroTimespec(&last_ref_update);
@@ -181,11 +181,13 @@ 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;
our_leap_status = LEAP_Unsynchronised;
our_leap_sec = 0;
our_tai_offset = 0;
initialised = 1;
our_root_dispersion = 1.0;
our_root_delay = 1.0;
@@ -205,11 +207,11 @@ REF_Initialise(void)
our_skew = 1.0e-6 * file_skew_ppm;
if (our_skew < MIN_SKEW)
our_skew = MIN_SKEW;
LOG(LOGS_INFO, LOGF_Reference, "Frequency %.3f +/- %.3f ppm read from %s",
LOG(LOGS_INFO, "Frequency %.3f +/- %.3f ppm read from %s",
file_freq_ppm, file_skew_ppm, drift_file);
LCL_SetAbsoluteFrequency(our_frequency_ppm);
} else {
LOG(LOGS_WARN, LOGF_Reference, "Could not read valid frequency and skew from driftfile %s",
LOG(LOGS_WARN, "Could not read valid frequency and skew from driftfile %s",
drift_file);
}
fclose(in);
@@ -219,12 +221,12 @@ REF_Initialise(void)
if (our_frequency_ppm == 0.0) {
our_frequency_ppm = LCL_ReadAbsoluteFrequency();
if (our_frequency_ppm != 0.0) {
LOG(LOGS_INFO, LOGF_Reference, "Initial frequency %.3f ppm", our_frequency_ppm);
LOG(LOGS_INFO, "Initial frequency %.3f ppm", our_frequency_ppm);
}
}
logfileid = CNF_GetLogTracking() ? LOG_FileOpen("tracking",
" Date (UTC) Time IP Address St Freq ppm Skew ppm Offset L Co Offset sd Rem. corr.")
" Date (UTC) Time IP Address St Freq ppm Skew ppm Offset L Co Offset sd Rem. corr. Root delay Root disp. Max. error")
: -1;
max_update_skew = fabs(CNF_GetMaxUpdateSkew()) * 1.0e-6;
@@ -243,11 +245,11 @@ REF_Initialise(void)
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) == LEAP_InsertSecond &&
get_tz_leap(1356912000) == LEAP_Normal) {
LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname);
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, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname);
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
}
@@ -266,6 +268,7 @@ REF_Initialise(void)
fb_drift_timeout_id = 0;
}
UTI_ZeroTimespec(&our_ref_time);
UTI_ZeroTimespec(&last_ref_update);
last_ref_update_interval = 0.0;
@@ -322,23 +325,6 @@ REF_GetLeapMode(void)
return leap_mode;
}
/* ================================================== */
static double
Sqr(double x)
{
return x*x;
}
/* ================================================== */
#if 0
static double
Cube(double x)
{
return x*x*x;
}
#endif
/* ================================================== */
/* Update the drift coefficients to the file. */
@@ -364,7 +350,7 @@ update_drift_file(double freq_ppm, double skew)
out = fopen(temp_drift_file, "w");
if (!out) {
Free(temp_drift_file);
LOG(LOGS_WARN, LOGF_Reference, "Could not open temporary driftfile %s.tmp for writing",
LOG(LOGS_WARN, "Could not open temporary driftfile %s.tmp for writing",
drift_file);
return;
}
@@ -374,7 +360,7 @@ update_drift_file(double freq_ppm, double skew)
r2 = fclose(out);
if (r1 < 0 || r2) {
Free(temp_drift_file);
LOG(LOGS_WARN, LOGF_Reference, "Could not write to temporary driftfile %s.tmp",
LOG(LOGS_WARN, "Could not write to temporary driftfile %s.tmp",
drift_file);
return;
}
@@ -384,8 +370,7 @@ update_drift_file(double freq_ppm, double skew)
if (!stat(drift_file,&buf)) {
if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
chmod(temp_drift_file,buf.st_mode & 0777)) {
LOG(LOGS_WARN, LOGF_Reference,
"Could not change ownership or permissions of temporary driftfile %s.tmp",
LOG(LOGS_WARN, "Could not change ownership or permissions of temporary driftfile %s.tmp",
drift_file);
}
}
@@ -395,7 +380,7 @@ update_drift_file(double freq_ppm, double skew)
if (rename(temp_drift_file,drift_file)) {
unlink(temp_drift_file);
Free(temp_drift_file);
LOG(LOGS_WARN, LOGF_Reference, "Could not replace old driftfile %s with new one %s.tmp",
LOG(LOGS_WARN, "Could not replace old driftfile %s with new one %s.tmp",
drift_file,drift_file);
return;
}
@@ -443,8 +428,8 @@ update_fb_drifts(double freq_ppm, double update_interval)
(freq_ppm - fb_drifts[i].freq);
}
DEBUG_LOG(LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds",
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
DEBUG_LOG("Fallback drift %d updated: %f ppm %f seconds",
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
}
}
@@ -457,7 +442,7 @@ fb_drift_timeout(void *arg)
fb_drift_timeout_id = 0;
DEBUG_LOG(LOGF_Reference, "Fallback drift %d active: %f ppm",
DEBUG_LOG("Fallback drift %d active: %f ppm",
next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq);
LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq);
REF_SetUnsynchronised();
@@ -492,14 +477,14 @@ schedule_fb_drift(struct timespec *now)
if (c > next_fb_drift) {
LCL_SetAbsoluteFrequency(fb_drifts[c - fb_drift_min].freq);
next_fb_drift = c;
DEBUG_LOG(LOGF_Reference, "Fallback drift %d set", c);
DEBUG_LOG("Fallback drift %d set", c);
}
if (i <= fb_drift_max) {
next_fb_drift = i;
UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i);
DEBUG_LOG("Fallback drift %d scheduled", i);
}
}
@@ -526,29 +511,36 @@ maybe_log_offset(double offset, time_t now)
double abs_offset;
FILE *p;
char buffer[BUFLEN], host[BUFLEN];
struct tm stm;
struct tm *tm;
abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, LOGF_Reference,
"System clock wrong by %.6f seconds, adjustment started",
LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
-offset);
}
if (do_mail_change &&
(abs_offset > mail_change_threshold)) {
snprintf(buffer, sizeof(buffer), "%s %." S_MAX_USER_LEN "s", MAIL_PROGRAM, mail_change_user);
snprintf(buffer, sizeof (buffer), "%s -t", MAIL_PROGRAM);
p = popen(buffer, "w");
if (p) {
if (gethostname(host, sizeof(host)) < 0) {
strcpy(host, "<UNKNOWN>");
}
host[sizeof (host) - 1] = '\0';
fprintf(p, "To: %s\n", mail_change_user);
fprintf(p, "Subject: chronyd reports change to system clock on node [%s]\n", host);
fputs("\n", p);
stm = *localtime(&now);
strftime(buffer, sizeof(buffer), "On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", &stm);
fputs(buffer, p);
tm = localtime(&now);
if (tm) {
strftime(buffer, sizeof (buffer),
"On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", tm);
fputs(buffer, p);
}
/* If offset < 0 the local clock is slow, so we are applying a
positive change to it to bring it into line, hence the
negation of 'offset' in the next statement (and earlier) */
@@ -558,8 +550,7 @@ maybe_log_offset(double offset, time_t now)
-offset, mail_change_threshold);
pclose(p);
} else {
LOG(LOGS_ERR, LOGF_Reference,
"Could not send mail notification to user %s\n",
LOG(LOGS_ERR, "Could not send mail notification to user %s\n",
mail_change_user);
}
}
@@ -594,7 +585,7 @@ is_offset_ok(double offset)
offset = fabs(offset);
if (offset > max_offset) {
LOG(LOGS_WARN, LOGF_Reference,
LOG(LOGS_WARN,
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
if (!max_offset_ignore)
@@ -609,7 +600,14 @@ is_offset_ok(double offset)
/* ================================================== */
static int
is_leap_second_day(struct tm *stm) {
is_leap_second_day(time_t when)
{
struct tm *stm;
stm = gmtime(&when);
if (!stm)
return 0;
/* Allow leap second only on the last day of June and December */
return (stm->tm_mon == 5 && stm->tm_mday == 30) ||
(stm->tm_mon == 11 && stm->tm_mday == 31);
@@ -618,12 +616,18 @@ is_leap_second_day(struct tm *stm) {
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when)
get_tz_leap(time_t when, int *tai_offset)
{
struct tm stm;
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)
@@ -631,12 +635,14 @@ get_tz_leap(time_t when)
last_tz_leap_check = when;
tz_leap = LEAP_Normal;
tz_tai_offset = 0;
stm = *gmtime(&when);
if (!is_leap_second_day(&stm))
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) {
@@ -647,6 +653,11 @@ get_tz_leap(time_t when)
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;
@@ -668,6 +679,8 @@ get_tz_leap(time_t when)
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
*tai_offset = tz_tai_offset;
return tz_leap;
}
@@ -678,10 +691,13 @@ leap_end_timeout(void *arg)
{
leap_timeout_id = 0;
leap_in_progress = 0;
if (our_tai_offset)
our_tai_offset += our_leap_sec;
our_leap_sec = 0;
if (leap_mode == REF_LeapModeSystem)
LCL_SetSystemLeap(0);
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
if (our_leap_status == LEAP_InsertSecond ||
our_leap_status == LEAP_DeleteSecond)
@@ -697,20 +713,20 @@ leap_start_timeout(void *arg)
switch (leap_mode) {
case REF_LeapModeSystem:
DEBUG_LOG(LOGF_Reference, "Waiting for system clock leap second correction");
DEBUG_LOG("Waiting for system clock leap second correction");
break;
case REF_LeapModeSlew:
LCL_NotifyLeap(our_leap_sec);
LCL_AccumulateOffset(our_leap_sec, 0.0);
LOG(LOGS_WARN, LOGF_Reference, "Adjusting system clock for leap second");
LOG(LOGS_WARN, "Adjusting system clock for leap second");
break;
case REF_LeapModeStep:
LCL_NotifyLeap(our_leap_sec);
LCL_ApplyStepOffset(our_leap_sec);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped for leap second");
LOG(LOGS_WARN, "System clock was stepped for leap second");
break;
case REF_LeapModeIgnore:
LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second");
LOG(LOGS_WARN, "Ignoring leap second");
break;
default:
break;
@@ -755,17 +771,22 @@ set_leap_timeout(time_t now)
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{
int leap_sec;
NTP_Leap tz_leap;
int leap_sec, tai_offset;
leap_sec = 0;
tai_offset = 0;
if (leap_tzname && now && leap == LEAP_Normal)
leap = get_tz_leap(now);
if (leap_tzname && now) {
tz_leap = get_tz_leap(now, &tai_offset);
if (leap == LEAP_Normal)
leap = tz_leap;
}
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
/* Check that leap second is allowed today */
if (is_leap_second_day(gmtime(&now))) {
if (is_leap_second_day(now)) {
if (leap == LEAP_InsertSecond) {
leap_sec = 1;
} else {
@@ -776,12 +797,14 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
}
}
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
&& !REF_IsLeapSecondClose()) {
our_leap_sec = leap_sec;
our_tai_offset = tai_offset;
switch (leap_mode) {
case REF_LeapModeSystem:
LCL_SetSystemLeap(our_leap_sec);
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
/* Fall through */
case REF_LeapModeSlew:
case REF_LeapModeStep:
@@ -801,18 +824,43 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
/* ================================================== */
static double
get_root_dispersion(struct timespec *ts)
{
if (UTI_IsZeroTimespec(&our_ref_time))
return 1.0;
return our_root_dispersion +
fabs(UTI_DiffTimespecsToDouble(ts, &our_ref_time)) *
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError());
}
/* ================================================== */
static void
write_log(struct timespec *ref_time, char *ref, int stratum, NTP_Leap leap,
double freq, double skew, double offset, int combined_sources,
double offset_sd, double uncorrected_offset)
write_log(struct timespec *now, int combined_sources, double freq,
double offset, double offset_sd, double uncorrected_offset,
double orig_root_distance)
{
const char leap_codes[4] = {'N', '+', '-', '?'};
if (logfileid != -1) {
LOG_FileWrite(logfileid, "%s %-15s %2d %10.3f %10.3f %10.3e %1c %2d %10.3e %10.3e",
UTI_TimeToLogForm(ref_time->tv_sec), ref, stratum, freq, skew,
offset, leap_codes[leap], combined_sources, offset_sd,
uncorrected_offset);
}
double root_dispersion, max_error;
static double last_sys_offset = 0.0;
if (logfileid == -1)
return;
max_error = orig_root_distance + fabs(last_sys_offset);
root_dispersion = get_root_dispersion(now);
last_sys_offset = offset - uncorrected_offset;
LOG_FileWrite(logfileid,
"%s %-15s %2d %10.3f %10.3f %10.3e %1c %2d %10.3e %10.3e %10.3e %10.3e %10.3e",
UTI_TimeToLogForm(now->tv_sec),
our_ref_ip.family != IPADDR_UNSPEC ?
UTI_IPToString(&our_ref_ip) : UTI_RefidToString(our_ref_id),
our_stratum, freq, 1.0e6 * our_skew, offset,
leap_codes[our_leap_status], combined_sources, offset_sd,
uncorrected_offset, our_root_delay, root_dispersion, max_error);
}
/* ================================================== */
@@ -825,15 +873,14 @@ special_mode_sync(int valid, double offset)
switch (mode) {
case REF_ModeInitStepSlew:
if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew");
LOG(LOGS_WARN, "No suitable source for initstepslew");
end_ref_mode(0);
break;
}
step = fabs(offset) >= CNF_GetInitStepThreshold();
LOG(LOGS_INFO, LOGF_Reference,
"System's initial offset : %.6f seconds %s of true (%s)",
LOG(LOGS_INFO, "System's initial offset : %.6f seconds %s of true (%s)",
fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew");
if (step)
@@ -847,14 +894,14 @@ special_mode_sync(int valid, double offset)
case REF_ModeUpdateOnce:
case REF_ModePrintOnce:
if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for synchronisation");
LOG(LOGS_WARN, "No suitable source for synchronisation");
end_ref_mode(0);
break;
}
step = mode == REF_ModeUpdateOnce;
LOG(LOGS_INFO, LOGF_Reference, "System clock wrong by %.6f seconds (%s)",
LOG(LOGS_INFO, "System clock wrong by %.6f seconds (%s)",
-offset, step ? "step" : "ignored");
if (step)
@@ -873,35 +920,58 @@ special_mode_sync(int valid, double offset)
/* ================================================== */
void
REF_SetReference(int stratum,
NTP_Leap leap,
int combined_sources,
uint32_t ref_id,
IPAddr *ref_ip,
struct timespec *ref_time,
double offset,
double offset_sd,
double frequency,
double skew,
double root_delay,
double root_dispersion
)
static void
get_clock_estimates(int manual,
double measured_freq, double measured_skew,
double *estimated_freq, double *estimated_skew,
double *residual_freq)
{
double gain, expected_freq, expected_skew, extra_skew;
/* We assume that the local clock is running according to our previously
determined value */
expected_freq = 0.0;
expected_skew = our_skew;
/* Set new frequency based on weighted average of the expected and measured
skew. Disable updates that are based on totally unreliable frequency
information unless it is a manual reference. */
if (manual) {
gain = 1.0;
} else if (fabs(measured_skew) > max_update_skew) {
DEBUG_LOG("Skew %f too large to track", measured_skew);
gain = 0.0;
} else {
gain = 3.0 * SQUARE(expected_skew) /
(3.0 * SQUARE(expected_skew) + SQUARE(measured_skew));
}
gain = CLAMP(0.0, gain, 1.0);
*estimated_freq = expected_freq + gain * (measured_freq - expected_freq);
*residual_freq = measured_freq - *estimated_freq;
extra_skew = sqrt(SQUARE(expected_freq - *estimated_freq) * (1.0 - gain) +
SQUARE(measured_freq - *estimated_freq) * gain);
*estimated_skew = expected_skew + gain * (measured_skew - expected_skew) + extra_skew;
}
/* ================================================== */
void
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
double offset, double offset_sd,
double frequency, double frequency_sd, double skew,
double root_delay, double root_dispersion)
{
double previous_skew, new_skew;
double previous_freq, new_freq;
double old_weight, new_weight, sum_weight;
double delta_freq1, delta_freq2;
double skew1, skew2;
double our_offset;
double our_frequency;
double abs_freq_ppm;
double update_interval;
double elapsed;
double correction_rate;
double uncorrected_offset, accumulate_offset, step_offset;
double residual_frequency, local_abs_frequency;
double elapsed, update_interval, correction_rate, orig_root_distance;
struct timespec now, raw_now;
NTP_int64 ref_fuzz;
int manual;
assert(initialised);
@@ -911,39 +981,33 @@ REF_SetReference(int stratum,
return;
}
/* Guard against dividing by zero */
if (skew < MIN_SKEW)
skew = MIN_SKEW;
manual = leap == LEAP_Unsynchronised;
/* If we get a serious rounding error in the source stats regression
processing, there is a remote chance that the skew argument is a
'not a number'. If such a quantity gets propagated into the
machine's kernel clock variables, nasty things will happen ..
To guard against this we need to check whether the skew argument
is a reasonable real number. I don't think isnan, isinf etc are
platform independent, so the following algorithm is used. */
{
double t;
t = (skew + skew) / skew; /* Skew shouldn't be zero either */
if ((t < 1.9) || (t > 2.1)) {
LOG(LOGS_WARN, LOGF_Reference, "Bogus skew value encountered");
return;
}
}
LCL_ReadRawTime(&raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
our_offset = offset + elapsed * frequency;
offset += elapsed * frequency;
offset_sd += elapsed * frequency_sd;
if (!is_offset_ok(our_offset))
if (last_ref_update.tv_sec) {
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
update_interval = MAX(update_interval, 0.0);
} else {
update_interval = 0.0;
}
/* Get new estimates of the frequency and skew including the new data */
get_clock_estimates(manual, frequency, skew,
&frequency, &skew, &residual_frequency);
if (!is_offset_ok(offset))
return;
are_we_synchronised = leap != LEAP_Unsynchronised ? 1 : 0;
orig_root_distance = our_root_delay / 2.0 + get_root_dispersion(&now);
are_we_synchronised = leap != LEAP_Unsynchronised;
our_stratum = stratum + 1;
our_ref_id = ref_id;
if (ref_ip)
@@ -951,17 +1015,13 @@ REF_SetReference(int stratum,
else
our_ref_ip.family = IPADDR_UNSPEC;
our_ref_time = *ref_time;
our_skew = skew;
our_residual_freq = residual_frequency;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
if (last_ref_update.tv_sec) {
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
if (update_interval < 0.0)
update_interval = 0.0;
} else {
update_interval = 0.0;
}
last_ref_update = now;
last_ref_update_interval = update_interval;
last_offset = offset;
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
@@ -979,68 +1039,28 @@ REF_SetReference(int stratum,
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */
if (is_step_limit_reached(our_offset, uncorrected_offset)) {
if (is_step_limit_reached(offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */
accumulate_offset = uncorrected_offset;
step_offset = our_offset - uncorrected_offset;
step_offset = offset - uncorrected_offset;
} else {
accumulate_offset = our_offset;
accumulate_offset = offset;
step_offset = 0.0;
}
/* Eliminate updates that are based on totally unreliable frequency
information. Ignore this limit with manual reference. */
if (fabs(skew) < max_update_skew || leap == LEAP_Unsynchronised) {
previous_skew = our_skew;
new_skew = skew;
previous_freq = 0.0; /* We assume that the local clock is running
according to our previously determined
value; note that this is a delta frequency
--- absolute frequencies are only known in
the local module. */
new_freq = frequency;
/* Set new frequency based on weighted average of old and new skew. With
manual reference the old frequency has no weight. */
old_weight = leap != LEAP_Unsynchronised ? 1.0 / Sqr(previous_skew) : 0.0;
new_weight = 3.0 / Sqr(new_skew);
sum_weight = old_weight + new_weight;
our_frequency = (previous_freq * old_weight + new_freq * new_weight) / sum_weight;
delta_freq1 = previous_freq - our_frequency;
delta_freq2 = new_freq - our_frequency;
skew1 = sqrt((Sqr(delta_freq1) * old_weight + Sqr(delta_freq2) * new_weight) / sum_weight);
skew2 = (previous_skew * old_weight + new_skew * new_weight) / sum_weight;
our_skew = skew1 + skew2;
our_residual_freq = new_freq - our_frequency;
LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
/* Adjust the clock */
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
} else {
DEBUG_LOG(LOGF_Reference, "Skew %f too large to track, offset=%f", skew, accumulate_offset);
LCL_AccumulateOffset(accumulate_offset, correction_rate);
our_residual_freq = frequency;
}
update_leap_status(leap, raw_now.tv_sec, 0);
maybe_log_offset(our_offset, raw_now.tv_sec);
maybe_log_offset(offset, raw_now.tv_sec);
if (step_offset != 0.0) {
if (LCL_ApplyStepOffset(step_offset))
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
}
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
LCL_SetSyncStatus(are_we_synchronised, offset_sd,
root_delay / 2.0 + get_root_dispersion(&now));
/* Add a random error of up to one second to the reference time to make it
less useful when disclosed to NTP and cmdmon clients for estimating
@@ -1051,44 +1071,33 @@ REF_SetReference(int stratum,
if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
our_ref_time.tv_sec--;
abs_freq_ppm = LCL_ReadAbsoluteFrequency();
local_abs_frequency = LCL_ReadAbsoluteFrequency();
write_log(&now,
our_ref_ip.family != IPADDR_UNSPEC ? UTI_IPToString(&our_ref_ip) : UTI_RefidToString(our_ref_id),
our_stratum,
our_leap_status,
abs_freq_ppm,
1.0e6*our_skew,
our_offset,
combined_sources,
offset_sd,
uncorrected_offset);
write_log(&now, combined_sources, local_abs_frequency,
offset, offset_sd, uncorrected_offset, orig_root_distance);
if (drift_file) {
/* Update drift file at most once per hour */
drift_file_age += update_interval;
if (drift_file_age < 0.0 || drift_file_age > 3600.0) {
update_drift_file(abs_freq_ppm, our_skew);
update_drift_file(local_abs_frequency, our_skew);
drift_file_age = 0.0;
}
}
/* Update fallback drifts */
if (fb_drifts) {
update_fb_drifts(abs_freq_ppm, update_interval);
if (fb_drifts && are_we_synchronised) {
update_fb_drifts(local_abs_frequency, update_interval);
schedule_fb_drift(&now);
}
last_ref_update_interval = update_interval;
last_offset = our_offset;
/* Update the moving average of squares of offset, quickly on start */
if (avg2_moving) {
avg2_offset += 0.1 * (our_offset * our_offset - avg2_offset);
avg2_offset += 0.1 * (SQUARE(offset) - avg2_offset);
} else {
if (avg2_offset > 0.0 && avg2_offset < our_offset * our_offset)
if (avg2_offset > 0.0 && avg2_offset < SQUARE(offset))
avg2_moving = 1;
avg2_offset = our_offset * our_offset;
avg2_offset = SQUARE(offset);
}
}
@@ -1107,7 +1116,7 @@ REF_SetManualReference
only supposed to be used with the local source option, really.
Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */
REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL,
ref_time, offset, 0.0, frequency, skew, 0.0, 0.0);
ref_time, offset, 0.0, frequency, skew, skew, 0.0, 0.0);
}
/* ================================================== */
@@ -1136,20 +1145,15 @@ REF_SetUnsynchronised(void)
}
update_leap_status(LEAP_Unsynchronised, 0, 0);
our_ref_ip.family = IPADDR_INET4;
our_ref_ip.addr.in4 = 0;
our_stratum = 0;
are_we_synchronised = 0;
LCL_SetSyncStatus(0, 0.0, 0.0);
write_log(&now,
"0.0.0.0",
0,
our_leap_status,
LCL_ReadAbsoluteFrequency(),
1.0e6*our_skew,
0.0,
0,
0.0,
uncorrected_offset);
write_log(&now, 0, LCL_ReadAbsoluteFrequency(), 0.0, 0.0, uncorrected_offset,
our_root_delay / 2.0 + get_root_dispersion(&now));
}
/* ================================================== */
@@ -1167,14 +1171,12 @@ REF_GetReferenceParams
double *root_dispersion
)
{
double elapsed, dispersion;
double dispersion;
assert(initialised);
if (are_we_synchronised) {
elapsed = UTI_DiffTimespecsToDouble(local_time, &our_ref_time);
dispersion = our_root_dispersion +
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
dispersion = get_root_dispersion(local_time);
} else {
dispersion = 0.0;
}
@@ -1214,7 +1216,7 @@ REF_GetReferenceParams
*leap_status = LEAP_Normal;
*root_delay = 0.0;
*root_dispersion = LCL_GetSysPrecisionAsQuantum();
*root_dispersion = 0.0;
} else {
@@ -1332,6 +1334,18 @@ int REF_IsLeapSecondClose(void)
/* ================================================== */
int
REF_GetTaiOffset(struct timespec *ts)
{
int tai_offset;
get_tz_leap(ts->tv_sec, &tai_offset);
return tai_offset;
}
/* ================================================== */
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{
@@ -1348,7 +1362,7 @@ REF_GetTrackingReport(RPT_TrackingReport *rep)
&rep->ref_id, &rep->ref_time,
&rep->root_delay, &rep->root_dispersion);
if (rep->stratum == NTP_MAX_STRATUM)
if (rep->stratum == NTP_MAX_STRATUM && !synchronised)
rep->stratum = 0;
rep->ip_addr.family = IPADDR_UNSPEC;

View File

@@ -144,6 +144,7 @@ extern void REF_SetReference
double offset,
double offset_sd,
double frequency,
double frequency_sd,
double skew,
double root_delay,
double root_dispersion
@@ -184,6 +185,9 @@ extern void REF_DisableLocal(void);
and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void);
/* Return TAI-UTC offset corresponding to a time in UTC if available */
extern int REF_GetTaiOffset(struct timespec *ts);
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
#endif /* GOT_REFERENCE_H */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011
* Copyright (C) Miroslav Lichvar 2011, 2016-2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,7 @@
#include "logging.h"
#include "util.h"
#define MAX_POINTS 128
#define MAX_POINTS 64
void
RGR_WeightedRegression
@@ -285,7 +285,7 @@ RGR_FindBestRegression
n - start <= min_samples) {
if (start != resid_start) {
/* Ignore extra samples in returned nruns */
nruns = n_runs_from_residuals(resid - resid_start + start, n - start);
nruns = n_runs_from_residuals(resid + (start - resid_start), n - start);
}
break;
} else {
@@ -340,7 +340,7 @@ RGR_FindBestRegression
0-521-43108-5). */
static double
find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
{
int u, v, l, r;
double temp;
@@ -403,9 +403,9 @@ find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
static double
find_ordered_entry(double *x, int n, int index)
{
int flags[MAX_POINTS];
char flags[MAX_POINTS];
memset(flags, 0, n * sizeof(int));
memset(flags, 0, n * sizeof (flags[0]));
return find_ordered_entry_with_flags(x, n, index, flags);
}
#endif
@@ -417,9 +417,9 @@ static double
find_median(double *x, int n)
{
int k;
int flags[MAX_POINTS];
char flags[MAX_POINTS];
memset(flags, 0, n*sizeof(int));
memset(flags, 0, n * sizeof (flags[0]));
k = n>>1;
if (n&1) {
return find_ordered_entry_with_flags(x, n, k, flags);
@@ -429,6 +429,19 @@ find_median(double *x, int n)
}
}
/* ================================================== */
double
RGR_FindMedian(double *x, int n)
{
double tmp[MAX_POINTS];
assert(n > 0 && n <= MAX_POINTS);
memcpy(tmp, x, n * sizeof (tmp[0]));
return find_median(tmp, n);
}
/* ================================================== */
/* This function evaluates the equation
@@ -509,7 +522,7 @@ RGR_FindBestRobustRegression
double mx, dx, my, dy;
int nruns = 0;
assert(n < MAX_POINTS);
assert(n <= MAX_POINTS);
if (n < 2) {
return 0;
@@ -566,24 +579,18 @@ RGR_FindBestRobustRegression
Estimate standard deviation of b and expand range about b based
on that. */
sb = sqrt(s2 * W/V);
if (sb > tol) {
incr = 3.0 * sb;
} else {
incr = 3.0 * tol;
}
incr = MAX(sb, tol);
blo = b;
bhi = b;
do {
/* Make sure incr is significant to blo and bhi */
while (bhi + incr == bhi || blo - incr == blo) {
incr *= 2;
}
incr *= 2.0;
/* Give up if the interval is too large */
if (incr > 100.0)
return 0;
blo = b - incr;
bhi = b + incr;
blo -= incr;
bhi += incr;
/* We don't want 'a' yet */
eval_robust_residual(x + start, y + start, n_points, blo, &a, &rlo);
eval_robust_residual(x + start, y + start, n_points, bhi, &a, &rhi);
@@ -594,6 +601,8 @@ RGR_FindBestRobustRegression
/* OK, so the root for b lies in (blo, bhi). Start bisecting */
do {
bmid = 0.5 * (blo + bhi);
if (!(blo < bmid && bmid < bhi))
break;
eval_robust_residual(x + start, y + start, n_points, bmid, &a, &rmid);
if (rmid == 0.0) {
break;
@@ -606,7 +615,7 @@ RGR_FindBestRobustRegression
} else {
assert(0);
}
} while ((bhi - blo) > tol && (bmid - blo) * (bhi - bmid) > 0.0);
} while (bhi - blo > tol);
*b0 = a;
*b1 = bmid;

View File

@@ -131,4 +131,7 @@ RGR_MultipleRegress
double *b2 /* estimated second slope */
);
/* Return the median value from an array */
extern double RGR_FindMedian(double *x, int n);
#endif /* GOT_REGRESS_H */

6
rtc.c
View File

@@ -104,7 +104,7 @@ apply_driftfile_time(time_t t)
if (now.tv_sec < t) {
if (LCL_ApplyStepOffset(now.tv_sec - t))
LOG(LOGS_INFO, LOGF_Rtc, "System time restored from driftfile");
LOG(LOGS_INFO, "System time restored from driftfile");
}
}
@@ -142,7 +142,7 @@ RTC_Initialise(int initial_set)
if (file_name) {
if (CNF_GetRtcSync()) {
LOG_FATAL(LOGF_Rtc, "rtcfile directive cannot be used with rtcsync");
LOG_FATAL("rtcfile directive cannot be used with rtcsync");
}
if (driver.init) {
@@ -150,7 +150,7 @@ RTC_Initialise(int initial_set)
driver_initialised = 1;
}
} else {
LOG(LOGS_ERR, LOGF_Rtc, "RTC not supported on this operating system");
LOG(LOGS_ERR, "RTC not supported on this operating system");
}
}
}

View File

@@ -189,7 +189,7 @@ accumulate_sample(time_t rtc, struct timespec *sys)
/* Discard all samples if the RTC was stepped back (not our trim) */
if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) {
DEBUG_LOG(LOGF_RtcLinux, "RTC samples discarded");
DEBUG_LOG("RTC samples discarded");
n_samples = 0;
}
@@ -294,7 +294,7 @@ slew_samples
coef_gain_rate += dfreq * (1.0 - coef_gain_rate);
}
DEBUG_LOG(LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
DEBUG_LOG("dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
dfreq, doffset,
old_seconds_fast, 1.0e6 * old_gain_rate,
coef_seconds_fast, 1.0e6 * coef_gain_rate);
@@ -352,7 +352,7 @@ rtc_from_t(const time_t *t)
static time_t
t_from_rtc(struct tm *stm) {
struct tm temp1, temp2;
struct tm temp1, temp2, *tm;
long diff;
time_t t1, t2;
@@ -360,18 +360,20 @@ t_from_rtc(struct tm *stm) {
temp1.tm_isdst = 0;
t1 = mktime(&temp1);
if (rtc_on_utc) {
temp2 = *gmtime(&t1);
} else {
temp2 = *localtime(&t1);
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
if (!tm) {
DEBUG_LOG("gmtime()/localtime() failed");
return -1;
}
temp2 = *tm;
temp2.tm_isdst = 0;
t2 = mktime(&temp2);
diff = t2 - t1;
if (t1 - diff == -1)
DEBUG_LOG(LOGF_RtcLinux, "Could not convert RTC time");
DEBUG_LOG("Could not convert RTC time");
return t1 - diff;
}
@@ -390,7 +392,7 @@ read_hwclock_file(const char *hwclock_file)
in = fopen(hwclock_file, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open %s : %s",
LOG(LOGS_WARN, "Could not open %s : %s",
hwclock_file, strerror(errno));
return;
}
@@ -408,8 +410,7 @@ read_hwclock_file(const char *hwclock_file)
} else if (i == 3 && !strncmp(line, "UTC", 3)) {
rtc_on_utc = 1;
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read RTC LOCAL/UTC setting from %s",
hwclock_file);
LOG(LOGS_WARN, "Could not read RTC LOCAL/UTC setting from %s", hwclock_file);
}
}
@@ -451,8 +452,7 @@ read_coefs_from_file(void)
&file_ref_offset,
&file_rate_ppm) == 4) {
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from %s",
coefs_file_name);
LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
}
fclose(in);
}
@@ -485,7 +485,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
out = fopen(temp_coefs_file_name, "w");
if (!out) {
Free(temp_coefs_file_name);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open temporary RTC file %s.tmp for writing",
LOG(LOGS_WARN, "Could not open temporary RTC file %s.tmp for writing",
coefs_file_name);
return RTC_ST_BADFILE;
}
@@ -496,7 +496,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
r2 = fclose(out);
if (r1 < 0 || r2) {
Free(temp_coefs_file_name);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not write to temporary RTC file %s.tmp",
LOG(LOGS_WARN, "Could not write to temporary RTC file %s.tmp",
coefs_file_name);
return RTC_ST_BADFILE;
}
@@ -506,7 +506,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
if (!stat(coefs_file_name,&buf)) {
if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) ||
chmod(temp_coefs_file_name,buf.st_mode & 0777)) {
LOG(LOGS_WARN, LOGF_RtcLinux,
LOG(LOGS_WARN,
"Could not change ownership or permissions of temporary RTC file %s.tmp",
coefs_file_name);
}
@@ -517,7 +517,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
if (rename(temp_coefs_file_name,coefs_file_name)) {
unlink(temp_coefs_file_name);
Free(temp_coefs_file_name);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not replace old RTC file %s.tmp with new one %s",
LOG(LOGS_WARN, "Could not replace old RTC file %s.tmp with new one %s",
coefs_file_name, coefs_file_name);
return RTC_ST_BADFILE;
}
@@ -550,7 +550,7 @@ RTC_Linux_Initialise(void)
fd = open (CNF_GetRtcDevice(), O_RDWR);
if (fd < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open RTC device %s : %s",
LOG(LOGS_ERR, "Could not open RTC device %s : %s",
CNF_GetRtcDevice(), strerror(errno));
return 0;
}
@@ -611,16 +611,14 @@ switch_interrupts(int onoff)
if (onoff) {
status = ioctl(fd, RTC_UIE_ON, 0);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
"enable", strerror(errno));
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "enable", strerror(errno));
return;
}
skip_interrupts = 1;
} else {
status = ioctl(fd, RTC_UIE_OFF, 0);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
"disable", strerror(errno));
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "disable", strerror(errno));
return;
}
}
@@ -658,7 +656,7 @@ set_rtc(time_t new_rtc_time)
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not set RTC time");
LOG(LOGS_ERR, "Could not set RTC time");
}
}
@@ -701,10 +699,10 @@ handle_initial_trim(void)
sys_error_now = rtc_error_now - coef_seconds_fast;
LCL_AccumulateOffset(sys_error_now, 0.0);
LOG(LOGS_INFO, LOGF_RtcLinux, "System clock off from RTC by %f seconds (slew)",
LOG(LOGS_INFO, "System clock off from RTC by %f seconds (slew)",
sys_error_now);
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid rtcfile coefficients");
LOG(LOGS_WARN, "No valid rtcfile coefficients");
}
coefs_valid = 0;
@@ -729,7 +727,7 @@ handle_relock_after_trim(void)
if (valid) {
write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
} else {
DEBUG_LOG(LOGF_RtcLinux, "Could not do regression after trim");
DEBUG_LOG("Could not do regression after trim");
}
coefs_valid = 0;
@@ -824,7 +822,7 @@ read_from_device(int fd_, int event, void *any)
if (status < 0) {
/* 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, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
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);
@@ -848,7 +846,7 @@ read_from_device(int fd_, int event, void *any)
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
error = 1;
goto turn_off_interrupt;
}
@@ -889,7 +887,7 @@ turn_off_interrupt:
switch (operating_mode) {
case OM_INITIAL:
if (error) {
DEBUG_LOG(LOGF_RtcLinux, "Could not complete initial step due to errors");
DEBUG_LOG("Could not complete initial step due to errors");
operating_mode = OM_NORMAL;
(after_init_hook)(after_init_hook_arg);
@@ -902,7 +900,7 @@ turn_off_interrupt:
case OM_AFTERTRIM:
if (error) {
DEBUG_LOG(LOGF_RtcLinux, "Could not complete after trim relock due to errors");
DEBUG_LOG("Could not complete after trim relock due to errors");
operating_mode = OM_NORMAL;
switch_interrupts(0);
@@ -1041,7 +1039,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
return 0;
}
@@ -1050,7 +1048,7 @@ RTC_Linux_TimePreInit(time_t driftfile_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, LOGF_RtcLinux, "System time set from RTC");
LOG(LOGS_INFO, "System time set from RTC");
}
} else {
return 0;
@@ -1095,7 +1093,7 @@ RTC_Linux_Trim(void)
if (fabs(coef_seconds_fast) > 1.0) {
LOG(LOGS_INFO, LOGF_RtcLinux, "RTC wrong by %.3f seconds (step)",
LOG(LOGS_INFO, "RTC wrong by %.3f seconds (step)",
coef_seconds_fast);
/* Do processing to set clock. Let R be the value we set the

453
samplefilt.c Normal file
View File

@@ -0,0 +1,453 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2014, 2016, 2018
*
* 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.
*
**********************************************************************
=======================================================================
Routines implementing a median sample filter.
*/
#include "config.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "regress.h"
#include "samplefilt.h"
#include "util.h"
#define MIN_SAMPLES 1
#define MAX_SAMPLES 256
struct SPF_Instance_Record {
int min_samples;
int max_samples;
int index;
int used;
int last;
int avg_var_n;
double avg_var;
double max_var;
double combine_ratio;
NTP_Sample *samples;
int *selected;
double *x_data;
double *y_data;
double *w_data;
};
/* ================================================== */
SPF_Instance
SPF_CreateInstance(int min_samples, int max_samples, double max_dispersion, double combine_ratio)
{
SPF_Instance filter;
filter = MallocNew(struct SPF_Instance_Record);
min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
max_samples = MAX(min_samples, max_samples);
combine_ratio = CLAMP(0.0, combine_ratio, 1.0);
filter->min_samples = min_samples;
filter->max_samples = max_samples;
filter->index = -1;
filter->used = 0;
filter->last = -1;
/* Set the first estimate to the system precision */
filter->avg_var_n = 0;
filter->avg_var = SQUARE(LCL_GetSysPrecisionAsQuantum());
filter->max_var = SQUARE(max_dispersion);
filter->combine_ratio = combine_ratio;
filter->samples = MallocArray(NTP_Sample, filter->max_samples);
filter->selected = MallocArray(int, filter->max_samples);
filter->x_data = MallocArray(double, filter->max_samples);
filter->y_data = MallocArray(double, filter->max_samples);
filter->w_data = MallocArray(double, filter->max_samples);
return filter;
}
/* ================================================== */
void
SPF_DestroyInstance(SPF_Instance filter)
{
Free(filter->samples);
Free(filter->selected);
Free(filter->x_data);
Free(filter->y_data);
Free(filter->w_data);
Free(filter);
}
/* ================================================== */
/* Check that samples times are strictly increasing */
static int
check_sample(SPF_Instance filter, NTP_Sample *sample)
{
if (filter->used <= 0)
return 1;
if (UTI_CompareTimespecs(&filter->samples[filter->last].time, &sample->time) >= 0) {
DEBUG_LOG("filter non-increasing sample time %s", UTI_TimespecToString(&sample->time));
return 0;
}
return 1;
}
/* ================================================== */
int
SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample)
{
if (!check_sample(filter, sample))
return 0;
filter->index++;
filter->index %= filter->max_samples;
filter->last = filter->index;
if (filter->used < filter->max_samples)
filter->used++;
filter->samples[filter->index] = *sample;
DEBUG_LOG("filter sample %d t=%s offset=%.9f peer_disp=%.9f",
filter->index, UTI_TimespecToString(&sample->time),
sample->offset, sample->peer_dispersion);
return 1;
}
/* ================================================== */
int
SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample)
{
if (filter->last < 0)
return 0;
*sample = filter->samples[filter->last];
return 1;
}
/* ================================================== */
int
SPF_GetNumberOfSamples(SPF_Instance filter)
{
return filter->used;
}
/* ================================================== */
double
SPF_GetAvgSampleDispersion(SPF_Instance filter)
{
return sqrt(filter->avg_var);
}
/* ================================================== */
void
SPF_DropSamples(SPF_Instance filter)
{
filter->index = -1;
filter->used = 0;
}
/* ================================================== */
static const NTP_Sample *tmp_sort_samples;
static int
compare_samples(const void *a, const void *b)
{
const NTP_Sample *s1, *s2;
s1 = &tmp_sort_samples[*(int *)a];
s2 = &tmp_sort_samples[*(int *)b];
if (s1->offset < s2->offset)
return -1;
else if (s1->offset > s2->offset)
return 1;
return 0;
}
/* ================================================== */
static int
select_samples(SPF_Instance filter)
{
int i, j, k, o, from, to, *selected;
double min_dispersion;
if (filter->used < filter->min_samples)
return 0;
selected = filter->selected;
/* With 4 or more samples, select those that have peer dispersion smaller
than 1.5x of the minimum dispersion */
if (filter->used > 4) {
for (i = 1, min_dispersion = filter->samples[0].peer_dispersion; i < filter->used; i++) {
if (min_dispersion > filter->samples[i].peer_dispersion)
min_dispersion = filter->samples[i].peer_dispersion;
}
for (i = j = 0; i < filter->used; i++) {
if (filter->samples[i].peer_dispersion <= 1.5 * min_dispersion)
selected[j++] = i;
}
} else {
j = 0;
}
if (j < 4) {
/* Select all samples */
for (j = 0; j < filter->used; j++)
selected[j] = j;
}
/* And sort their indices by offset */
tmp_sort_samples = filter->samples;
qsort(selected, j, sizeof (int), compare_samples);
/* Select samples closest to the median */
if (j > 2) {
from = j * (1.0 - filter->combine_ratio) / 2.0;
from = CLAMP(1, from, (j - 1) / 2);
} else {
from = 0;
}
to = j - from;
/* Mark unused samples and sort the rest by their time */
o = filter->used - filter->index - 1;
for (i = 0; i < from; i++)
selected[i] = -1;
for (; i < to; i++)
selected[i] = (selected[i] + o) % filter->used;
for (; i < filter->used; i++)
selected[i] = -1;
for (i = from; i < to; i++) {
j = selected[i];
selected[i] = -1;
while (j != -1 && selected[j] != j) {
k = selected[j];
selected[j] = j;
j = k;
}
}
for (i = j = 0, k = -1; i < filter->used; i++) {
if (selected[i] != -1)
selected[j++] = (selected[i] + filter->used - o) % filter->used;
}
assert(j > 0 && j <= filter->max_samples);
return j;
}
/* ================================================== */
static int
combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
{
double mean_peer_dispersion, mean_root_dispersion, mean_peer_delay, mean_root_delay;
double mean_x, mean_y, disp, var, prev_avg_var;
NTP_Sample *sample, *last_sample;
int i, dof;
last_sample = &filter->samples[filter->selected[n - 1]];
/* Prepare data */
for (i = 0; i < n; i++) {
sample = &filter->samples[filter->selected[i]];
filter->x_data[i] = UTI_DiffTimespecsToDouble(&sample->time, &last_sample->time);
filter->y_data[i] = sample->offset;
filter->w_data[i] = sample->peer_dispersion;
}
/* Calculate mean offset and interval since the last sample */
for (i = 0, mean_x = mean_y = 0.0; i < n; i++) {
mean_x += filter->x_data[i];
mean_y += filter->y_data[i];
}
mean_x /= n;
mean_y /= n;
if (n >= 4) {
double b0, b1, s2, sb0, sb1;
/* Set y axis to the mean sample time */
for (i = 0; i < n; i++)
filter->x_data[i] -= mean_x;
/* Make a linear fit and use the estimated standard deviation of the
intercept as dispersion */
RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
&b0, &b1, &s2, &sb0, &sb1);
var = s2;
disp = sb0;
dof = n - 2;
} else if (n >= 2) {
for (i = 0, disp = 0.0; i < n; i++)
disp += (filter->y_data[i] - mean_y) * (filter->y_data[i] - mean_y);
var = disp / (n - 1);
disp = sqrt(var);
dof = n - 1;
} else {
var = filter->avg_var;
disp = sqrt(var);
dof = 1;
}
/* Avoid working with zero dispersion */
if (var < 1e-20) {
var = 1e-20;
disp = sqrt(var);
}
/* Drop the sample if the variance is larger than the maximum */
if (filter->max_var > 0.0 && var > filter->max_var) {
DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
sqrt(var), sqrt(filter->max_var));
return 0;
}
prev_avg_var = filter->avg_var;
/* Update the exponential moving average of the variance */
if (filter->avg_var_n > 50) {
filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
} else {
filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
(dof + filter->avg_var_n);
if (filter->avg_var_n == 0)
prev_avg_var = filter->avg_var;
filter->avg_var_n += dof;
}
/* Use the long-term average of variance instead of the estimated value
unless it is significantly smaller in order to reduce the noise in
sourcestats weights */
if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
disp = sqrt(filter->avg_var) * disp / sqrt(var);
mean_peer_dispersion = mean_root_dispersion = mean_peer_delay = mean_root_delay = 0.0;
for (i = 0; i < n; i++) {
sample = &filter->samples[filter->selected[i]];
mean_peer_dispersion += sample->peer_dispersion;
mean_root_dispersion += sample->root_dispersion;
mean_peer_delay += sample->peer_delay;
mean_root_delay += sample->root_delay;
}
mean_peer_dispersion /= n;
mean_root_dispersion /= n;
mean_peer_delay /= n;
mean_root_delay /= n;
UTI_AddDoubleToTimespec(&last_sample->time, mean_x, &result->time);
result->offset = mean_y;
result->peer_dispersion = MAX(disp, mean_peer_dispersion);
result->root_dispersion = MAX(disp, mean_root_dispersion);
result->peer_delay = mean_peer_delay;
result->root_delay = mean_root_delay;
result->stratum = last_sample->stratum;
result->leap = last_sample->leap;
return 1;
}
/* ================================================== */
int
SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
{
int n;
n = select_samples(filter);
if (n < 1)
return 0;
if (!combine_selected_samples(filter, n, sample))
return 0;
SPF_DropSamples(filter);
return 1;
}
/* ================================================== */
void
SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double doffset)
{
int i, first, last;
double delta_time;
if (filter->last < 0)
return;
/* Always slew the last sample as it may be returned even if no new
samples were accumulated */
if (filter->used > 0) {
first = 0;
last = filter->used - 1;
} else {
first = last = filter->last;
}
for (i = first; i <= last; i++) {
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
&delta_time, dfreq, doffset);
filter->samples[i].offset -= delta_time;
}
}
/* ================================================== */
void
SPF_AddDispersion(SPF_Instance filter, double dispersion)
{
int i;
for (i = 0; i < filter->used; i++) {
filter->samples[i].peer_dispersion += dispersion;
filter->samples[i].root_dispersion += dispersion;
}
}

49
samplefilt.h Normal file
View File

@@ -0,0 +1,49 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for sample filter.
*/
#ifndef GOT_SAMPLEFILT_H
#define GOT_SAMPLEFILT_H
#include "ntp.h"
typedef struct SPF_Instance_Record *SPF_Instance;
extern SPF_Instance SPF_CreateInstance(int min_samples, int max_samples,
double max_dispersion, double combine_ratio);
extern void SPF_DestroyInstance(SPF_Instance filter);
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
extern void SPF_DropSamples(SPF_Instance filter);
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
double dfreq, double doffset);
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
#endif

34
sched.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2013-2015
* Copyright (C) Miroslav Lichvar 2011, 2013-2016
*
* 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
@@ -163,7 +163,7 @@ SCH_AddFileHandler
assert(fd >= 0);
if (fd >= FD_SETSIZE)
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
LOG_FATAL("Too many file descriptors");
/* Resize the array if the descriptor is highest so far */
while (ARR_GetSize(file_handlers) <= fd) {
@@ -219,13 +219,16 @@ SCH_RemoveFileHandler(int fd)
/* ================================================== */
void
SCH_SetFileHandlerEvents(int fd, int events)
SCH_SetFileHandlerEvent(int fd, int event, int enable)
{
FileHandlerEntry *ptr;
assert(events);
ptr = ARR_GetElement(file_handlers, fd);
ptr->events = events;
if (enable)
ptr->events |= event;
else
ptr->events &= ~event;
}
/* ================================================== */
@@ -350,7 +353,7 @@ SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArg
LCL_ReadRawTime(&now);
UTI_AddDoubleToTimespec(&now, delay, &then);
if (UTI_CompareTimespecs(&now, &then) > 0) {
LOG_FATAL(LOGF_Scheduler, "Timeout overflow");
LOG_FATAL("Timeout overflow");
}
return SCH_AddTimeout(&then, handler, arg);
@@ -512,7 +515,7 @@ dispatch_timeouts(struct timespec *now) {
more time than was delay of a scheduled timeout. */
if (n_done > n_timer_queue_entries * 4 &&
n_done > n_entries_on_start * 4) {
LOG_FATAL(LOGF_Scheduler, "Possible infinite loop in scheduling");
LOG_FATAL("Possible infinite loop in scheduling");
}
}
}
@@ -531,7 +534,8 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
if (except_fds && FD_ISSET(fd, except_fds)) {
/* This descriptor has an exception, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
if (ptr->handler)
(ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
nfd--;
/* Don't try to read from it now */
@@ -544,14 +548,16 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
if (read_fds && FD_ISSET(fd, read_fds)) {
/* This descriptor can be read from, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
if (ptr->handler)
(ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
nfd--;
}
if (write_fds && FD_ISSET(fd, write_fds)) {
/* This descriptor can be written to, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
if (ptr->handler)
(ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
nfd--;
}
}
@@ -680,10 +686,10 @@ check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
raw->tv_sec + JUMP_DETECT_THRESHOLD) {
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected!");
LOG(LOGS_WARN, "Backward time jump detected!");
} else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
raw->tv_sec) {
LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!");
LOG(LOGS_WARN, "Forward time jump detected!");
} else {
return 1;
}
@@ -742,7 +748,7 @@ SCH_MainLoop(void)
/* if there are no file descriptors being waited on and no
timeout set, this is clearly ridiculous, so stop the run */
if (!ptv && !p_read_fds && !p_write_fds)
LOG_FATAL(LOGF_Scheduler, "Nothing to do");
LOG_FATAL("Nothing to do");
status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
errsv = errno;
@@ -762,7 +768,7 @@ SCH_MainLoop(void)
if (status < 0) {
if (!need_to_exit && errsv != EINTR) {
LOG_FATAL(LOGF_Scheduler, "select() failed : %s", strerror(errsv));
LOG_FATAL("select() failed : %s", strerror(errsv));
}
} else if (status > 0) {
/* A file descriptor is ready for input or output */

View File

@@ -34,7 +34,8 @@ typedef unsigned int SCH_TimeoutID;
typedef enum {
SCH_ReservedTimeoutValue = 0,
SCH_NtpSamplingClass,
SCH_NtpClientClass,
SCH_NtpPeerClass,
SCH_NtpBroadcastClass,
SCH_NumberOfClasses /* needs to be last */
} SCH_TimeoutClass;
@@ -59,7 +60,7 @@ extern void SCH_Finalise(void);
/* Register a handler for when select goes true on a file descriptor */
extern void SCH_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
extern void SCH_RemoveFileHandler(int fd);
extern void SCH_SetFileHandlerEvents(int fd, int events);
extern void SCH_SetFileHandlerEvent(int fd, int event, int enable);
/* Get the time stamp taken after a file descriptor became ready or a timeout expired */
extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);

View File

@@ -144,7 +144,7 @@ update_stages(void)
is equal to the offset that should be smoothed out */
s1 = smooth_offset / max_wander;
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
s2 = SQUARE(smooth_freq) / (2.0 * SQUARE(max_wander));
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. The direction of the 1st stage is selected so that
@@ -208,7 +208,7 @@ update_stages(void)
stages[2].length = l3;
for (i = 0; i < NUM_STAGES; i++) {
DEBUG_LOG(LOGF_Smooth, "Smooth stage %d wander %e length %f",
DEBUG_LOG("Smooth stage %d wander %e length %f",
i + 1, stages[i].wander, stages[i].length);
}
}
@@ -230,7 +230,7 @@ update_smoothing(struct timespec *now, double offset, double freq)
update_stages();
DEBUG_LOG(LOGF_Smooth, "Smooth offset %e freq %e", smooth_offset, smooth_freq);
DEBUG_LOG("Smooth offset %e freq %e", smooth_offset, smooth_freq);
}
static void
@@ -246,7 +246,8 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
update_smoothing(cooked, doffset, dfreq);
}
UTI_AdjustTimespec(&last_update, cooked, &last_update, &delta, dfreq, doffset);
if (!UTI_IsZeroTimespec(&last_update))
UTI_AdjustTimespec(&last_update, cooked, &last_update, &delta, dfreq, doffset);
}
void SMT_Initialise(void)
@@ -264,6 +265,8 @@ void SMT_Initialise(void)
max_freq *= 1e-6;
max_wander *= 1e-6;
UTI_ZeroTimespec(&last_update);
LCL_AddParameterChangeHandler(handle_slew, NULL);
}
@@ -295,7 +298,7 @@ SMT_Activate(struct timespec *now)
if (!enabled || !locked)
return;
LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated%s", leap_only_mode ?
LOG(LOGS_INFO, "Time smoothing activated%s", leap_only_mode ?
" (leap seconds only)" : "");
locked = 0;
last_update = *now;

142
sources.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016
* Copyright (C) Miroslav Lichvar 2011-2016, 2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -54,6 +54,7 @@ static int initialised = 0;
/* ================================================== */
/* Structure used to hold info for selecting between sources */
struct SelectInfo {
NTP_Leap leap;
int stratum;
int select_ok;
double std_dev;
@@ -91,7 +92,6 @@ typedef enum {
source */
struct SRC_Instance_Record {
SST_Stats stats;
NTP_Leap leap_status; /* Leap status */
int index; /* Index back into the array of source */
uint32_t ref_id; /* The reference ID of this source
(i.e. from its IP address, NOT the
@@ -213,7 +213,9 @@ void SRC_Finalise(void)
/* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, IPAddr *addr, int min_samples, int max_samples)
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options,
IPAddr *addr, int min_samples, int max_samples,
double min_delay, double asymmetry)
{
SRC_Instance result;
@@ -225,7 +227,8 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_optio
max_samples = CNF_GetMaxSamples();
result = MallocNew(struct SRC_Instance_Record);
result->stats = SST_CreateInstance(ref_id, addr, min_samples, max_samples);
result->stats = SST_CreateInstance(ref_id, addr, min_samples, max_samples,
min_delay, asymmetry);
if (n_sources == max_n_sources) {
/* Reallocate memory */
@@ -288,7 +291,6 @@ void SRC_DestroyInstance(SRC_Instance instance)
void
SRC_ResetInstance(SRC_Instance instance)
{
instance->leap_status = LEAP_Normal;
instance->active = 0;
instance->updates = 0;
instance->reachability = 0;
@@ -327,39 +329,24 @@ SRC_GetSourcestats(SRC_Instance instance)
This function causes the frequency estimation to be re-run for the
designated source, and the clock selection procedure to be re-run
afterwards.
Parameters are described in sources.h
*/
void SRC_AccumulateSample
(SRC_Instance inst,
struct timespec *sample_time,
double offset,
double peer_delay,
double peer_dispersion,
double root_delay,
double root_dispersion,
int stratum,
NTP_Leap leap_status)
void
SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
{
assert(initialised);
inst->leap_status = leap_status;
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
root_delay, root_dispersion, stratum);
DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
sample->root_delay, sample->root_dispersion, sample->stratum);
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
LOG(LOGS_INFO, "Dropping sample around leap second");
return;
}
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
IS FLIPPED */
SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
SST_AccumulateSample(inst->stats, sample);
SST_DoNewRegression(inst->stats);
}
@@ -451,7 +438,7 @@ log_selection_message(char *format, char *arg)
{
if (REF_GetMode() != REF_ModeNormal)
return;
LOG(LOGS_INFO, LOGF_Sources, format, arg);
LOG(LOGS_INFO, format, arg);
}
/* ================================================== */
@@ -509,20 +496,21 @@ mark_ok_sources(SRC_Status status)
static int
combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
double *offset_sd, double *frequency, double *skew)
double *offset_sd, double *frequency, double *frequency_sd, double *skew)
{
struct timespec src_ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew;
double frequency_weight, sum_frequency_weight, sum_frequency;
double inv_sum2_frequency_sd, inv_sum2_skew;
int i, index, combined;
if (n_sel_sources == 1)
return 1;
sum_offset_weight = sum_offset = sum2_offset_sd = 0.0;
sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0;
sum_frequency_weight = sum_frequency = inv_sum2_frequency_sd = inv_sum2_skew = 0.0;
sel_src_distance = sources[selected_source_index]->sel_info.root_distance;
if (sources[selected_source_index]->type == SRC_NTP)
@@ -532,7 +520,7 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
index = sel_sources[i];
SST_GetTrackingData(sources[index]->stats, &src_ref_time,
&src_offset, &src_offset_sd,
&src_frequency, &src_skew,
&src_frequency, &src_frequency_sd, &src_skew,
&src_root_delay, &src_root_dispersion);
/* Don't include this source if its distance is longer than the distance of
@@ -560,20 +548,23 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency;
src_offset_sd += elapsed * src_frequency_sd;
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / src_skew;
frequency_weight = 1.0 / SQUARE(src_frequency_sd);
DEBUG_LOG(LOGF_Sources, "combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd,
frequency_weight, src_frequency, src_frequency_sd, src_skew);
sum_offset_weight += offset_weight;
sum_offset += offset_weight * src_offset;
sum2_offset_sd += offset_weight * (src_offset_sd * src_offset_sd +
(src_offset - *offset) * (src_offset - *offset));
sum2_offset_sd += offset_weight * (SQUARE(src_offset_sd) +
SQUARE(src_offset - *offset));
sum_frequency_weight += frequency_weight;
sum_frequency += frequency_weight * src_frequency;
inv_sum2_skew += 1.0 / (src_skew * src_skew);
inv_sum2_frequency_sd += 1.0 / SQUARE(src_frequency_sd);
inv_sum2_skew += 1.0 / SQUARE(src_skew);
combined++;
}
@@ -582,10 +573,11 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
*offset = sum_offset / sum_offset_weight;
*offset_sd = sqrt(sum2_offset_sd / sum_offset_weight);
*frequency = sum_frequency / sum_frequency_weight;
*frequency_sd = 1.0 / sqrt(inv_sum2_frequency_sd);
*skew = 1.0 / sqrt(inv_sum2_skew);
DEBUG_LOG(LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e",
*offset, *offset_sd, *frequency, *skew);
DEBUG_LOG("combined result offset=%e osd=%e freq=%e fsd=%e skew=%e",
*offset, *offset_sd, *frequency, *frequency_sd, *skew);
return combined;
}
@@ -599,12 +591,12 @@ 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;
int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
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 depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score;
double first_sample_ago, max_reach_sample_ago;
@@ -632,6 +624,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_badstats_sources = 0;
sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0;
max_sel_reach_size = 0;
max_reach_sample_ago = 0.0;
for (i = 0; i < n_sources; i++) {
@@ -649,7 +642,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, &si->leap,
&si->lo_limit, &si->hi_limit, &si->root_distance,
&si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok);
@@ -662,6 +655,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* Include extra dispersion in the root distance of sources that don't
have new samples (the last sample is older than span of all samples) */
if (first_sample_ago < 2.0 * si->last_sample_ago) {
double extra_disp = LCL_GetMaxClockError() *
(2.0 * si->last_sample_ago - first_sample_ago);
si->root_distance += extra_disp;
si->lo_limit -= extra_disp;
si->hi_limit += extra_disp;
}
/* Require the root distance to be below the allowed maximum */
if (si->root_distance > max_distance) {
sources[i]->status = SRC_BAD_DISTANCE;
@@ -681,6 +684,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (max_sel_reach < sources[i]->reachability)
max_sel_reach = sources[i]->reachability;
if (max_sel_reach_size < sources[i]->reachability_size)
max_sel_reach_size = sources[i]->reachability_size;
}
orphan_stratum = REF_GetOrphanStratum();
@@ -736,12 +742,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
uint32_t local_ref_id = NSR_GetLocalRefid(sources[orphan_source]->ip_addr);
if (!local_ref_id) {
LOG(LOGS_ERR, LOGF_Sources, "Unknown local refid in orphan mode");
LOG(LOGS_ERR, "Unknown local refid in orphan mode");
} else if (sources[orphan_source]->ref_id < local_ref_id) {
sources[orphan_source]->status = SRC_OK;
n_sel_sources = 1;
DEBUG_LOG(LOGF_Sources, "selecting orphan refid=%"PRIx32,
sources[orphan_source]->ref_id);
DEBUG_LOG("selecting orphan refid=%"PRIx32, sources[orphan_source]->ref_id);
}
}
@@ -765,18 +770,17 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints += 2;
}
DEBUG_LOG(LOGF_Sources, "badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f",
n_badstats_sources, n_sel_sources, max_badstat_reach,
max_sel_reach, max_reach_sample_ago);
DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f",
n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach,
(unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago);
/* Wait for the next call if we have no source selected and there is
a source with bad stats (has less than 3 samples) with reachability
equal to shifted maximum reachability of sources with valid stats.
This delays selecting source on start with servers using the same
polling interval until they all have valid stats. */
if (n_badstats_sources && n_sel_sources &&
selected_source_index == INVALID_SOURCE &&
max_sel_reach >> 1 == max_badstat_reach) {
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);
return;
}
@@ -917,9 +921,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
continue;
leap_votes++;
if (sources[index]->leap_status == LEAP_InsertSecond)
if (sources[index]->sel_info.leap == LEAP_InsertSecond)
leap_ins++;
else if (sources[index]->leap_status == LEAP_DeleteSecond)
else if (sources[index]->sel_info.leap == LEAP_DeleteSecond)
leap_del++;
}
@@ -1003,7 +1007,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
sources[i]->sel_score = 1.0 / distance;
}
DEBUG_LOG(LOGF_Sources, "select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f",
DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f",
sources[i]->sel_score, sources[i]->ref_id,
updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance);
@@ -1027,7 +1031,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[max_score_index]->updates == 0) {
selected_source_index = INVALID_SOURCE;
mark_ok_sources(SRC_WAITS_UPDATE);
DEBUG_LOG(LOGF_Sources, "best source has no updates");
DEBUG_LOG("best source has no updates");
return;
}
@@ -1066,18 +1070,18 @@ SRC_SelectSource(SRC_Instance updated_inst)
SST_GetTrackingData(sources[selected_source_index]->stats, &ref_time,
&src_offset, &src_offset_sd,
&src_frequency, &src_skew,
&src_frequency, &src_frequency_sd, &src_skew,
&src_root_delay, &src_root_dispersion);
combined = combine_sources(n_sel_sources, &ref_time, &src_offset,
&src_offset_sd, &src_frequency, &src_skew);
combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd,
&src_frequency, &src_frequency_sd, &src_skew);
REF_SetReference(sources[selected_source_index]->sel_info.stratum,
leap_status, combined,
sources[selected_source_index]->ref_id,
sources[selected_source_index]->ip_addr,
&ref_time, src_offset, src_offset_sd,
src_frequency, src_skew,
src_frequency, src_frequency_sd, src_skew,
src_root_delay, src_root_dispersion);
}
@@ -1098,7 +1102,7 @@ SRC_SetReselectDistance(double distance)
{
if (reselect_distance != distance) {
reselect_distance = distance;
LOG(LOGS_INFO, LOGF_Sources, "New reselect distance %f", distance);
LOG(LOGS_INFO, "New reselect distance %f", distance);
}
}
@@ -1153,7 +1157,7 @@ FILE *open_dumpfile(SRC_Instance inst, const char *mode)
dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0') {
LOG(LOGS_WARN, LOGF_Sources, "dumpdir not specified");
LOG(LOGS_WARN, "dumpdir not specified");
return NULL;
}
@@ -1164,13 +1168,13 @@ FILE *open_dumpfile(SRC_Instance inst, const char *mode)
(inst->type != SRC_NTP &&
snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat",
dumpdir, inst->ref_id) >= sizeof (filename))) {
LOG(LOGS_WARN, LOGF_Sources, "dumpdir too long");
LOG(LOGS_WARN, "dumpdir too long");
return NULL;
}
f = fopen(filename, mode);
if (!f && mode[0] != 'r')
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file for %s",
LOG(LOGS_WARN, "Could not open dump file for %s",
source_to_string(inst));
return f;
@@ -1207,10 +1211,10 @@ SRC_ReloadSources(void)
if (!in)
continue;
if (!SST_LoadFromFile(sources[i]->stats, in))
LOG(LOGS_WARN, LOGF_Sources, "Could not load dump file for %s",
LOG(LOGS_WARN, "Could not load dump file for %s",
source_to_string(sources[i]));
else
LOG(LOGS_INFO, LOGF_Sources, "Loaded dump file for %s",
LOG(LOGS_INFO, "Loaded dump file for %s",
source_to_string(sources[i]));
fclose(in);
}
@@ -1248,7 +1252,7 @@ SRC_RemoveDumpFiles(void)
if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
continue;
DEBUG_LOG(LOGF_Sources, "Removing %s", gl.gl_pathv[i]);
DEBUG_LOG("Removing %s", gl.gl_pathv[i]);
unlink(gl.gl_pathv[i]);
}

View File

@@ -59,7 +59,9 @@ typedef enum {
/* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, IPAddr *addr, int min_samples, int max_samples);
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options,
IPAddr *addr, int min_samples, int max_samples,
double min_delay, double asymmetry);
/* Function to get rid of a source when it is being unconfigured.
This may cause the current reference source to be reselected, if this
@@ -78,34 +80,8 @@ extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
/* This function is called by one of the source drivers when it has
a new sample that is to be accumulated.
This function causes the frequency estimation to be re-run for the
designated source, and the clock selection procedure to be re-run
afterwards.
sample_time is the local time at which the sample is to be
considered to have been made, in terms of doing a regression fit of
offset against local time.
offset is the offset at the time, in seconds. Positive indicates
that the local clock is SLOW relative to the source, negative
indicates that the local clock is FAST relative to it.
root_delay and root_dispersion are in seconds, and are as per
RFC 5905. root_dispersion only includes the peer's root dispersion
+ local sampling precision + skew dispersion accrued during the
measurement. It is the job of the source statistics algorithms +
track.c to add on the extra dispersion due to the residual standard
deviation of the offsets from this source after regression, to form
the root_dispersion field in the packets transmitted to clients or
peers.
stratum is the stratum of the source that supplied the sample.
*/
extern void SRC_AccumulateSample(SRC_Instance instance, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
a new sample that is to be accumulated */
extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample);
/* This routine sets the source as receiving reachability updates */
extern void SRC_SetActive(SRC_Instance inst);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014
* Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018
*
* 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
@@ -51,8 +51,8 @@
#define MIN_SKEW 1.0e-12
#define MAX_SKEW 1.0e+02
/* The minimum assumed std dev for weighting */
#define MIN_WEIGHT_SD 1.0e-9
/* The minimum standard deviation */
#define MIN_STDDEV 1.0e-9
/* The asymmetry of network jitter when all jitter is in one direction */
#define MAX_ASYMMETRY 0.5
@@ -85,6 +85,12 @@ struct SST_Stats_Record {
int min_samples;
int max_samples;
/* User defined minimum delay */
double fixed_min_delay;
/* User defined asymmetry of network jitter */
double fixed_asymmetry;
/* Number of samples currently stored. The samples are stored in circular
buffer. */
int n_samples;
@@ -126,6 +132,7 @@ struct SST_Stats_Record {
source per unit local time. (Positive => local clock fast,
negative => local clock slow) */
double estimated_frequency;
double estimated_frequency_sd;
/* This is the assumed worst case bounds on the estimated frequency.
We assume that the true frequency lies within +/- half this much
@@ -168,10 +175,11 @@ struct SST_Stats_Record {
time of the measurements */
double root_dispersions[MAX_SAMPLES];
/* This array contains the strata that were associated with the sources
at the times the samples were generated */
int strata[MAX_SAMPLES];
/* The stratum from the last accumulated sample */
int stratum;
/* The leap status from the last accumulated sample */
NTP_Leap leap;
};
/* ================================================== */
@@ -200,13 +208,16 @@ SST_Finalise(void)
/* This function creates a new instance of the statistics handler */
SST_Stats
SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_samples)
SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_samples,
double min_delay, double asymmetry)
{
SST_Stats inst;
inst = MallocNew(struct SST_Stats_Record);
inst->min_samples = min_samples;
inst->max_samples = max_samples;
inst->fixed_min_delay = min_delay;
inst->fixed_asymmetry = asymmetry;
SST_SetRefid(inst, refid, addr);
SST_ResetInstance(inst);
@@ -235,7 +246,8 @@ SST_ResetInstance(SST_Stats inst)
inst->best_single_sample = 0;
inst->min_delay_sample = 0;
inst->estimated_frequency = 0;
inst->skew = 2000.0e-6;
inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
inst->skew = WORST_CASE_FREQ_BOUND;
inst->estimated_offset = 0.0;
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
UTI_ZeroTimespec(&inst->offset_time);
@@ -243,6 +255,7 @@ SST_ResetInstance(SST_Stats inst)
inst->nruns = 0;
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
inst->leap = LEAP_Unsynchronised;
}
/* ================================================== */
@@ -278,11 +291,7 @@ prune_register(SST_Stats inst, int new_oldest)
/* ================================================== */
void
SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
double offset,
double peer_delay, double peer_dispersion,
double root_delay, double root_dispersion,
int stratum)
SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
{
int n, m;
@@ -294,8 +303,8 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
/* Make sure it's newer than the last sample */
if (inst->n_samples &&
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s",
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], &sample->time) >= 0) {
LOG(LOGS_WARN, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
SST_ResetInstance(inst);
}
@@ -304,15 +313,21 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
(MAX_SAMPLES * REGRESS_RUNS_RATIO);
m = n % MAX_SAMPLES;
inst->sample_times[n] = *sample_time;
inst->offsets[n] = offset;
inst->orig_offsets[m] = offset;
inst->peer_delays[n] = peer_delay;
inst->peer_dispersions[m] = peer_dispersion;
inst->root_delays[m] = root_delay;
inst->root_dispersions[m] = root_dispersion;
inst->strata[m] = stratum;
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
IS FLIPPED */
inst->sample_times[n] = sample->time;
inst->offsets[n] = -sample->offset;
inst->orig_offsets[m] = -sample->offset;
inst->peer_delays[n] = sample->peer_delay;
inst->peer_dispersions[m] = sample->peer_dispersion;
inst->root_delays[m] = sample->root_delay;
inst->root_dispersions[m] = sample->root_dispersion;
inst->stratum = sample->stratum;
inst->leap = sample->leap;
if (inst->peer_delays[n] < inst->fixed_min_delay)
inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n];
if (!inst->n_samples || inst->peer_delays[n] < inst->peer_delays[inst->min_delay_sample])
inst->min_delay_sample = n;
@@ -418,45 +433,63 @@ find_min_delay_sample(SST_Stats inst)
minimum network delay. This can significantly improve the accuracy and
stability of the estimated offset and frequency. */
static int
estimate_asymmetry(double *times_back, double *offsets, double *delays, int n,
double *asymmetry, int *asymmetry_run)
{
double a;
/* Reset the counter when the regression fails or the sign changes */
if (!RGR_MultipleRegress(times_back, delays, offsets, n, &a) ||
a * *asymmetry_run < 0.0) {
*asymmetry = 0;
*asymmetry_run = 0.0;
return 0;
}
if (a <= -MIN_ASYMMETRY && *asymmetry_run > -MAX_ASYMMETRY_RUN)
(*asymmetry_run)--;
else if (a >= MIN_ASYMMETRY && *asymmetry_run < MAX_ASYMMETRY_RUN)
(*asymmetry_run)++;
if (abs(*asymmetry_run) < MIN_ASYMMETRY_RUN)
return 0;
*asymmetry = CLAMP(-MAX_ASYMMETRY, a, MAX_ASYMMETRY);
return 1;
}
/* ================================================== */
static void
correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
{
double asymmetry, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
double min_delay, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
int i, n;
/* Don't try to estimate the asymmetry with reference clocks */
if (!inst->ip_addr)
/* Check if the asymmetry was not specified to be zero */
if (inst->fixed_asymmetry == 0.0)
return;
min_delay = SST_MinRoundTripDelay(inst);
n = inst->runs_samples + inst->n_samples;
for (i = 0; i < n; i++)
delays[i] = inst->peer_delays[get_runsbuf_index(inst, i - inst->runs_samples)] -
inst->peer_delays[inst->min_delay_sample];
min_delay;
/* Reset the counter when the regression fails or the sign changes */
if (!RGR_MultipleRegress(times_back, delays, offsets, n, &asymmetry) ||
asymmetry * inst->asymmetry_run < 0.0) {
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
return;
if (fabs(inst->fixed_asymmetry) <= MAX_ASYMMETRY) {
inst->asymmetry = inst->fixed_asymmetry;
} else {
if (!estimate_asymmetry(times_back, offsets, delays, n,
&inst->asymmetry, &inst->asymmetry_run))
return;
}
asymmetry = CLAMP(-MAX_ASYMMETRY, asymmetry, MAX_ASYMMETRY);
if (asymmetry <= -MIN_ASYMMETRY && inst->asymmetry_run > -MAX_ASYMMETRY_RUN)
inst->asymmetry_run--;
else if (asymmetry >= MIN_ASYMMETRY && inst->asymmetry_run < MAX_ASYMMETRY_RUN)
inst->asymmetry_run++;
if (abs(inst->asymmetry_run) < MIN_ASYMMETRY_RUN)
return;
/* Correct the offsets */
for (i = 0; i < n; i++)
offsets[i] -= asymmetry * delays[i];
inst->asymmetry = asymmetry;
offsets[i] -= inst->asymmetry * delays[i];
}
/* ================================================== */
@@ -466,7 +499,7 @@ correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
time. E.g. a value of 4 means that we think the standard deviation
is four times the fluctuation of the peer distance */
#define SD_TO_DIST_RATIO 1.0
#define SD_TO_DIST_RATIO 0.7
/* ================================================== */
/* This function runs the linear regression operation on the data. It
@@ -486,9 +519,10 @@ SST_DoNewRegression(SST_Stats inst)
int best_start, times_back_start;
double est_intercept, est_slope, est_var, est_intercept_sd, est_slope_sd;
int i, j, nruns;
double min_distance, mean_distance;
double min_distance, median_distance;
double sd_weight, sd;
double old_skew, old_freq, stress;
double precision;
convert_to_intervals(inst, times_back + inst->runs_samples);
@@ -497,25 +531,29 @@ SST_DoNewRegression(SST_Stats inst)
offsets[i + inst->runs_samples] = inst->offsets[get_runsbuf_index(inst, i)];
}
for (i = 0, mean_distance = 0.0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
for (i = 0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
j = get_buf_index(inst, i);
peer_distances[i] = 0.5 * inst->peer_delays[get_runsbuf_index(inst, i)] +
inst->peer_dispersions[j];
mean_distance += peer_distances[i];
if (peer_distances[i] < min_distance) {
min_distance = peer_distances[i];
}
}
mean_distance /= inst->n_samples;
/* And now, work out the weight vector */
sd = mean_distance - min_distance;
sd = CLAMP(MIN_WEIGHT_SD, sd, min_distance);
precision = LCL_GetSysPrecisionAsQuantum();
median_distance = RGR_FindMedian(peer_distances, inst->n_samples);
sd = (median_distance - min_distance) / SD_TO_DIST_RATIO;
sd = CLAMP(precision, sd, min_distance);
min_distance += precision;
for (i=0; i<inst->n_samples; i++) {
sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / sd;
weights[i] = sd_weight * sd_weight;
sd_weight = 1.0;
if (peer_distances[i] > min_distance)
sd_weight += (peer_distances[i] - min_distance) / sd;
weights[i] = SQUARE(sd_weight);
}
}
@@ -535,17 +573,18 @@ SST_DoNewRegression(SST_Stats inst)
old_freq = inst->estimated_frequency;
inst->estimated_frequency = est_slope;
inst->estimated_frequency_sd = CLAMP(MIN_SKEW, est_slope_sd, MAX_SKEW);
inst->skew = est_slope_sd * RGR_GetTCoef(degrees_of_freedom);
inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample];
inst->estimated_offset_sd = est_intercept_sd;
inst->std_dev = sqrt(est_var);
inst->std_dev = MAX(MIN_STDDEV, sqrt(est_var));
inst->nruns = nruns;
inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
DEBUG_LOG(LOGF_SourceStats, "off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
DEBUG_LOG("off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
inst->estimated_offset, inst->estimated_frequency, inst->skew,
inst->n_samples, best_start, inst->nruns,
inst->asymmetry, inst->asymmetry_run);
@@ -565,6 +604,7 @@ SST_DoNewRegression(SST_Stats inst)
prune_register(inst, best_start);
} else {
inst->estimated_frequency = 0.0;
inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
inst->skew = WORST_CASE_FREQ_BOUND;
times_back_start = 0;
}
@@ -601,7 +641,7 @@ SST_GetFrequencyRange(SST_Stats inst,
void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
@@ -621,10 +661,11 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
i = get_runsbuf_index(inst, inst->best_single_sample);
j = get_buf_index(inst, inst->best_single_sample);
*stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
*stratum = inst->stratum;
*leap = inst->leap;
*std_dev = inst->std_dev;
sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency;
*root_distance = 0.5 * inst->root_delays[j] +
inst->root_dispersions[j] + sample_elapsed * inst->skew;
@@ -653,7 +694,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
*select_ok = inst->regression_ok;
DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
inst->n_samples, offset, *root_distance, *std_dev,
*first_sample_ago, *last_sample_ago, *select_ok);
}
@@ -663,7 +704,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
void
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *frequency, double *frequency_sd, double *skew,
double *root_delay, double *root_dispersion)
{
int i, j;
@@ -678,15 +719,16 @@ SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
*average_offset = inst->estimated_offset;
*offset_sd = inst->estimated_offset_sd;
*frequency = inst->estimated_frequency;
*frequency_sd = inst->estimated_frequency_sd;
*skew = inst->skew;
*root_delay = inst->root_delays[j];
elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew, *average_offset, *offset_sd, *root_dispersion);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample + *offset_sd;
DEBUG_LOG("n=%d off=%f offsd=%f freq=%e freqsd=%e skew=%e delay=%f disp=%f",
inst->n_samples, *average_offset, *offset_sd,
*frequency, *frequency_sd, *skew, *root_delay, *root_dispersion);
}
/* ================================================== */
@@ -719,7 +761,7 @@ SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doff
inst->estimated_offset += delta_time;
inst->estimated_frequency = (inst->estimated_frequency - dfreq) / (1.0 - dfreq);
DEBUG_LOG(LOGF_SourceStats, "n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f",
DEBUG_LOG("n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f",
inst->n_samples, inst->runs_samples,
UTI_TimespecToString(&prev), UTI_TimespecToString(&inst->offset_time),
prev_offset, inst->estimated_offset,
@@ -768,47 +810,33 @@ SST_PredictOffset(SST_Stats inst, struct timespec *when)
double
SST_MinRoundTripDelay(SST_Stats inst)
{
if (inst->fixed_min_delay > 0.0)
return inst->fixed_min_delay;
if (!inst->n_samples)
return DBL_MAX;
return inst->peer_delays[inst->min_delay_sample];
}
/* ================================================== */
int
SST_IsGoodSample(SST_Stats inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timespec *when)
SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
double *last_sample_ago, double *predicted_offset,
double *min_delay, double *skew, double *std_dev)
{
double elapsed, allowed_increase, delay_increase;
if (inst->n_samples < 6)
return 0;
if (inst->n_samples < 3)
return 1;
*last_sample_ago = UTI_DiffTimespecsToDouble(sample_time, &inst->offset_time);
*predicted_offset = inst->estimated_offset +
*last_sample_ago * inst->estimated_frequency;
*min_delay = SST_MinRoundTripDelay(inst);
*skew = inst->skew;
*std_dev = inst->std_dev;
elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
/* Require that the ratio of the increase in delay from the minimum to the
standard deviation is less than max_delay_dev_ratio. In the allowed
increase in delay include also skew and clock_error. */
allowed_increase = inst->std_dev * max_delay_dev_ratio +
elapsed * (inst->skew + clock_error);
delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0;
if (delay_increase < allowed_increase)
return 1;
offset -= inst->estimated_offset + elapsed * inst->estimated_frequency;
/* Before we decide to drop the sample, make sure the difference between
measured offset and predicted offset is not significantly larger than
the increase in delay */
if (fabs(offset) - delay_increase > allowed_increase)
return 1;
DEBUG_LOG(LOGF_SourceStats, "Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f",
offset, delay, allowed_increase, delay_increase);
return 0;
return 1;
}
/* ================================================== */
@@ -842,7 +870,7 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
inst->root_delays[j],
inst->root_dispersions[j],
1.0, /* used to be inst->weights[i] */
inst->strata[j]);
inst->stratum /* used to be an array */);
}
@@ -865,7 +893,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
char line[1024];
double weight;
assert(!inst->n_samples);
SST_ResetInstance(inst);
if (fgets(line, sizeof(line), in) &&
sscanf(line, "%d", &inst->n_samples) == 1 &&
@@ -887,7 +915,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
&(inst->root_delays[i]),
&(inst->root_dispersions[i]),
&weight, /* not used anymore */
&(inst->strata[i])) != 10)) {
&inst->stratum) != 10)) {
/* This is the branch taken if the read FAILED */
@@ -914,7 +942,6 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
return 1;
inst->last_sample = inst->n_samples - 1;
inst->runs_samples = 0;
find_min_delay_sample(inst);
SST_DoNewRegression(inst);
@@ -936,7 +963,7 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->orig_latest_meas = inst->orig_offsets[j];
report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j];
report->stratum = inst->stratum;
/* Align the sample time to reduce the leak of the receive timestamp */
last_sample_time = inst->sample_times[i];

View File

@@ -38,7 +38,9 @@ extern void SST_Initialise(void);
extern void SST_Finalise(void);
/* This function creates a new instance of the statistics handler */
extern SST_Stats SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_samples);
extern SST_Stats SST_CreateInstance(uint32_t refid, IPAddr *addr,
int min_samples, int max_samples,
double min_delay, double asymmetry);
/* This function deletes an instance of the statistics handler. */
extern void SST_DeleteInstance(SST_Stats inst);
@@ -49,19 +51,8 @@ extern void SST_ResetInstance(SST_Stats inst);
/* This function changes the reference ID and IP address */
extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
/* This function accumulates a single sample into the statistics handler
sample_time is the epoch at which the sample is to be considered to
have been made.
offset is the offset of the local clock relative to the source in
seconds. Positive indicates that the local clock if FAST (contrary
to the NTP parts of the software)
stratum is the stratum of the source from which the sample came.
*/
extern void SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
/* This function accumulates a single sample into the statistics handler */
extern void SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample);
/* This function runs the linear regression operation on the data. It
finds the set of most recent samples that give the tightest
@@ -78,7 +69,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */
extern void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
@@ -91,7 +82,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
extern void
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *frequency, double *frequency_sd, double *skew,
double *root_delay, double *root_dispersion);
/* This routine is called when the local machine clock parameters are
@@ -124,10 +115,10 @@ extern double SST_PredictOffset(SST_Stats inst, struct timespec *when);
/* Find the minimum round trip delay in the register */
extern double SST_MinRoundTripDelay(SST_Stats inst);
/* This routine determines if a new sample is good enough that it should be
accumulated */
extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timespec *when);
/* Get data needed for testing NTP delay */
extern int SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
double *last_sample_ago, double *predicted_offset,
double *min_delay, double *skew, double *std_dev);
extern void SST_SaveToFile(SST_Stats inst, FILE *out);

View File

@@ -29,12 +29,19 @@
#include "sources.h"
typedef enum {
SRC_OFFLINE,
SRC_ONLINE,
SRC_MAYBE_ONLINE,
} SRC_Connectivity;
typedef struct {
int minpoll;
int maxpoll;
int online;
SRC_Connectivity connectivity;
int auto_offline;
int presend_minpoll;
int burst;
int iburst;
int min_stratum;
int poll_target;
@@ -42,12 +49,15 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
int filter_length;
int interleaved;
int sel_options;
uint32_t authkey;
double max_delay;
double max_delay_ratio;
double max_delay_dev_ratio;
double min_delay;
double asymmetry;
double offset;
} SourceParameters;
@@ -63,6 +73,7 @@ typedef struct {
#define SRC_DEFAULT_MAXSOURCES 4
#define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1)
#define SRC_DEFAULT_ASYMMETRY 1.0
#define INACTIVE_AUTHKEY 0
/* Flags for source selection */

12
stubs.c
View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014-2015
* Copyright (C) Miroslav Lichvar 2014-2016
*
* 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
@@ -91,7 +91,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->arg = anything;
if (pipe(inst->pipe))
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
LOG_FATAL("pipe() failed");
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
@@ -254,13 +254,7 @@ NSR_GetLocalRefid(IPAddr *address)
}
int
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
{
return 0;
}
int
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
return 0;
}

39
sys.c
View File

@@ -30,23 +30,36 @@
#include "sysincl.h"
#include "sys.h"
#include "sys_null.h"
#include "logging.h"
#if defined(LINUX)
#include "sys_linux.h"
#include "sys_posix.h"
#elif defined(SOLARIS)
#include "sys_solaris.h"
#include "sys_posix.h"
#elif defined(NETBSD) || defined(FREEBSD)
#include "sys_netbsd.h"
#include "sys_posix.h"
#elif defined(MACOSX)
#include "sys_macosx.h"
#endif
/* ================================================== */
static int null_driver;
/* ================================================== */
void
SYS_Initialise(void)
SYS_Initialise(int clock_control)
{
null_driver = !clock_control;
if (null_driver) {
SYS_Null_Initialise();
return;
}
#if defined(LINUX)
SYS_Linux_Initialise();
#elif defined(SOLARIS)
@@ -65,6 +78,10 @@ SYS_Initialise(void)
void
SYS_Finalise(void)
{
if (null_driver) {
SYS_Null_Finalise();
return;
}
#if defined(LINUX)
SYS_Linux_Finalise();
#elif defined(SOLARIS)
@@ -83,7 +100,7 @@ SYS_Finalise(void)
void SYS_DropRoot(uid_t uid, gid_t gid)
{
#if defined(LINUX) && defined (FEAT_PRIVDROP)
SYS_Linux_DropRoot(uid, gid);
SYS_Linux_DropRoot(uid, gid, !null_driver);
#elif defined(SOLARIS) && defined(FEAT_PRIVDROP)
SYS_Solaris_DropRoot(uid, gid);
#elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP)
@@ -91,7 +108,7 @@ void SYS_DropRoot(uid_t uid, gid_t gid)
#elif defined(MACOSX) && defined(FEAT_PRIVDROP)
SYS_MacOSX_DropRoot(uid, gid);
#else
LOG_FATAL(LOGF_Sys, "dropping root privileges not supported");
LOG_FATAL("dropping root privileges not supported");
#endif
}
@@ -102,7 +119,7 @@ void SYS_EnableSystemCallFilter(int level)
#if defined(LINUX) && defined(FEAT_SCFILTER)
SYS_Linux_EnableSystemCallFilter(level);
#else
LOG_FATAL(LOGF_Sys, "system call filter not supported");
LOG_FATAL("system call filter not supported");
#endif
}
@@ -110,12 +127,12 @@ void SYS_EnableSystemCallFilter(int level)
void SYS_SetScheduler(int SchedPriority)
{
#if defined(LINUX) && defined(HAVE_SCHED_SETSCHEDULER)
SYS_Linux_SetScheduler(SchedPriority);
#elif defined(MACOSX)
#if defined(MACOSX)
SYS_MacOSX_SetScheduler(SchedPriority);
#elif defined(HAVE_PTHREAD_SETSCHEDPARAM)
SYS_Posix_SetScheduler(SchedPriority);
#else
LOG_FATAL(LOGF_Sys, "scheduler priority setting not supported");
LOG_FATAL("scheduler priority setting not supported");
#endif
}
@@ -123,10 +140,10 @@ void SYS_SetScheduler(int SchedPriority)
void SYS_LockMemory(void)
{
#if defined(LINUX) && defined(HAVE_MLOCKALL)
SYS_Linux_MemLockAll(1);
#if defined(HAVE_MLOCKALL)
SYS_Posix_MemLockAll();
#else
LOG_FATAL(LOGF_Sys, "memory locking not supported");
LOG_FATAL("memory locking not supported");
#endif
}

2
sys.h
View File

@@ -30,7 +30,7 @@
#define GOT_SYS_H
/* Called at the start of the run to do initialisation */
extern void SYS_Initialise(void);
extern void SYS_Initialise(int clock_control);
/* Called at the end of the run to do final clean-up */
extern void SYS_Finalise(void);

View File

@@ -129,7 +129,7 @@ start_fastslew(void)
drv_accrue_offset(offset_register, 0.0);
DEBUG_LOG(LOGF_SysGeneric, "fastslew offset=%e", offset_register);
DEBUG_LOG("fastslew offset=%e", offset_register);
offset_register = 0.0;
fastslew_active = 1;
@@ -246,7 +246,7 @@ update_slew(void)
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
slew_start = now;
DEBUG_LOG(LOGF_SysGeneric, "slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
offset_register, correction_rate, base_freq, total_freq, slew_freq,
duration, slew_error);
}
@@ -333,7 +333,7 @@ apply_step_offset(double offset)
UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed");
DEBUG_LOG("settimeofday() failed");
return 0;
}

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
*
* 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,27 +33,14 @@
#include <sys/utsname.h>
#if defined(HAVE_SCHED_SETSCHEDULER)
# include <sched.h>
#endif
#if defined(HAVE_MLOCKALL)
# include <sys/mman.h>
#include <sys/resource.h>
#endif
#ifdef FEAT_PRIVDROP
#include <sys/prctl.h>
#include <sys/capability.h>
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_SCFILTER
#include <sys/prctl.h>
#include <seccomp.h>
#include <termios.h>
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_PPS
#include <linux/pps.h>
#endif
@@ -65,9 +52,15 @@
#endif
#endif
#ifdef FEAT_PRIVDROP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
#include "sys_linux.h"
#include "sys_timex.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "privops.h"
#include "util.h"
@@ -238,7 +231,7 @@ guess_hz(void)
}
/* oh dear. doomed. */
LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %d", tick);
LOG_FATAL("Can't determine hz from tick %d", tick);
return 0;
}
@@ -281,11 +274,11 @@ get_kernel_version(int *major, int *minor, int *patch)
struct utsname uts;
if (uname(&uts) < 0)
LOG_FATAL(LOGF_SysLinux, "uname() failed");
LOG_FATAL("uname() failed");
*patch = 0;
if (sscanf(uts.release, "%d.%d.%d", major, minor, patch) < 2)
LOG_FATAL(LOGF_SysLinux, "Could not parse kernel version");
LOG_FATAL("Could not parse kernel version");
}
/* ================================================== */
@@ -307,22 +300,28 @@ get_version_specific_details(void)
nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
max_tick_bias = nominal_tick / 10;
/* We can't reliably detect the internal kernel HZ, it may not even be fixed
(CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
tick_update_hz = 100;
/* In modern kernels the frequency of the clock is updated immediately in the
adjtimex() system call. Assume a maximum delay of 10 microseconds. */
tick_update_hz = 100000;
get_kernel_version(&major, &minor, &patch);
DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
LOG_FATAL(LOGF_SysLinux, "Kernel version not supported, sorry.");
LOG_FATAL("Kernel version not supported, sorry.");
}
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
/* Tickless kernels before 2.6.33 accumulated ticks only in
half-second intervals */
/* In tickless kernels before 2.6.33 the frequency is updated in
a half-second interval */
tick_update_hz = 2;
} else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
/* In kernels before 4.19 the frequency is updated only on internal ticks
(CONFIG_HZ). As their rate cannot be reliably detected from the user
space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
assume the lowest commonly used constant rate */
tick_update_hz = 100;
}
/* ADJ_SETOFFSET support */
@@ -332,8 +331,8 @@ get_version_specific_details(void)
have_setoffset = 1;
}
DEBUG_LOG(LOGF_SysLinux, "hz=%d nominal_tick=%d max_tick_bias=%d",
hz, nominal_tick, max_tick_bias);
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
hz, nominal_tick, max_tick_bias, tick_update_hz);
}
/* ================================================== */
@@ -378,6 +377,18 @@ test_step_offset(void)
return 1;
}
/* ================================================== */
static void
report_time_adjust_blockers(void)
{
#if defined(FEAT_PRIVDROP) && defined(CAP_IS_SUPPORTED)
if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
return;
LOG(LOGS_WARN, "CAP_SYS_TIME not present");
#endif
}
/* ================================================== */
/* Initialisation code for this module */
@@ -386,10 +397,12 @@ SYS_Linux_Initialise(void)
{
get_version_specific_details();
report_time_adjust_blockers();
reset_adjtime_offset();
if (have_setoffset && !test_step_offset()) {
LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET");
LOG(LOGS_INFO, "adjtimex() doesn't support ADJ_SETOFFSET");
have_setoffset = 0;
}
@@ -413,27 +426,30 @@ SYS_Linux_Finalise(void)
#ifdef FEAT_PRIVDROP
void
SYS_Linux_DropRoot(uid_t uid, gid_t gid)
SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
{
const char *cap_text;
char cap_text[256];
cap_t cap;
if (prctl(PR_SET_KEEPCAPS, 1)) {
LOG_FATAL(LOGF_SysLinux, "prctl() failed");
LOG_FATAL("prctl() failed");
}
UTI_DropRoot(uid, gid);
/* Keep CAP_NET_BIND_SERVICE only if NTP port can be opened */
cap_text = CNF_GetNTPPort() ?
"cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep";
/* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened
and keep CAP_SYS_TIME only if the clock control is enabled */
if (snprintf(cap_text, sizeof (cap_text), "%s %s",
CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "",
clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
assert(0);
if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL(LOGF_SysLinux, "cap_from_text() failed");
LOG_FATAL("cap_from_text() failed");
}
if (cap_set_proc(cap)) {
LOG_FATAL(LOGF_SysLinux, "cap_set_proc() failed");
LOG_FATAL("cap_set_proc() failed");
}
cap_free(cap);
@@ -452,7 +468,7 @@ void check_seccomp_applicability(void)
CNF_GetMailOnChange(&mail_enabled, &mail_threshold, &mail_user);
if (mail_enabled)
LOG_FATAL(LOGF_SysLinux, "mailonchange directive cannot be used with -F enabled");
LOG_FATAL("mailonchange directive cannot be used with -F enabled");
}
/* ================================================== */
@@ -465,29 +481,32 @@ SYS_Linux_EnableSystemCallFilter(int level)
SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday),
SCMP_SYS(settimeofday), SCMP_SYS(time),
/* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getrlimit),
SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigprocmask),
SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), SCMP_SYS(wait4),
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid),
SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn),
SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn),
SCMP_SYS(wait4), SCMP_SYS(waitpid),
/* Memory */
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2),
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
/* Filesystem */
SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32),
SCMP_SYS(_llseek), SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown),
SCMP_SYS(chown32), SCMP_SYS(faccessat), SCMP_SYS(fchmodat), SCMP_SYS(fchownat),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64),
SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
SCMP_SYS(lseek), SCMP_SYS(newfstatat), SCMP_SYS(rename), SCMP_SYS(renameat),
SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64),
SCMP_SYS(unlink), SCMP_SYS(unlinkat),
/* Socket */
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(getsockopt),
SCMP_SYS(recv), SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
SCMP_SYS(send), SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
/* TODO: check socketcall arguments */
SCMP_SYS(socketcall),
/* General I/O */
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe),
SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
SCMP_SYS(set_robust_list), SCMP_SYS(write),
SCMP_SYS(pipe2), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(pselect6), SCMP_SYS(read),
SCMP_SYS(futex), SCMP_SYS(select), SCMP_SYS(set_robust_list), SCMP_SYS(write),
/* Miscellaneous */
SCMP_SYS(uname),
SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname),
};
const int socket_domains[] = {
@@ -509,12 +528,21 @@ SYS_Linux_EnableSystemCallFilter(int level)
#endif
};
const static int fcntls[] = { F_GETFD, F_SETFD };
const static int fcntls[] = { F_GETFD, F_SETFD, F_SETFL };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_SYS_OFFSET,
PTP_EXTTS_REQUEST, PTP_SYS_OFFSET,
#ifdef PTP_PIN_SETFUNC
PTP_PIN_SETFUNC,
#endif
#ifdef PTP_SYS_OFFSET_EXTENDED
PTP_SYS_OFFSET_EXTENDED,
#endif
#ifdef PTP_SYS_OFFSET_PRECISE
PTP_SYS_OFFSET_PRECISE,
#endif
#endif
#ifdef FEAT_PPS
PPS_FETCH,
@@ -541,7 +569,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
if (ctx == NULL)
LOG_FATAL(LOGF_SysLinux, "Failed to initialize seccomp");
LOG_FATAL("Failed to initialize seccomp");
/* Add system calls that are always allowed */
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
@@ -582,76 +610,19 @@ SYS_Linux_EnableSystemCallFilter(int level)
}
if (seccomp_load(ctx) < 0)
LOG_FATAL(LOGF_SysLinux, "Failed to load seccomp rules");
LOG_FATAL("Failed to load seccomp rules");
LOG(LOGS_INFO, LOGF_SysLinux, "Loaded seccomp filter");
LOG(LOGS_INFO, "Loaded seccomp filter");
seccomp_release(ctx);
return;
add_failed:
LOG_FATAL(LOGF_SysLinux, "Failed to add seccomp rules");
LOG_FATAL("Failed to add seccomp rules");
}
#endif
/* ================================================== */
#if defined(HAVE_SCHED_SETSCHEDULER)
/* Install SCHED_FIFO real-time scheduler with specified priority */
void SYS_Linux_SetScheduler(int SchedPriority)
{
int pmax, pmin;
struct sched_param sched;
if (SchedPriority < 1 || SchedPriority > 99) {
LOG_FATAL(LOGF_SysLinux, "Bad scheduler priority: %d", SchedPriority);
} else {
sched.sched_priority = SchedPriority;
pmax = sched_get_priority_max(SCHED_FIFO);
pmin = sched_get_priority_min(SCHED_FIFO);
if ( SchedPriority > pmax ) {
sched.sched_priority = pmax;
}
else if ( SchedPriority < pmin ) {
sched.sched_priority = pmin;
}
if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) {
LOG(LOGS_ERR, LOGF_SysLinux, "sched_setscheduler() failed");
}
else {
DEBUG_LOG(LOGF_SysLinux, "Enabled SCHED_FIFO with priority %d",
sched.sched_priority);
}
}
}
#endif /* HAVE_SCHED_SETSCHEDULER */
#if defined(HAVE_MLOCKALL)
/* Lock the process into RAM so that it will never be swapped out */
void SYS_Linux_MemLockAll(int LockAll)
{
struct rlimit rlim;
if (LockAll == 1 ) {
/* Make sure that we will be able to lock all the memory we need */
/* even after dropping privileges. This does not actually reaerve any memory */
rlim.rlim_max = RLIM_INFINITY;
rlim.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
LOG(LOGS_ERR, LOGF_SysLinux, "setrlimit() failed: not locking into RAM");
}
else {
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
LOG(LOGS_ERR, LOGF_SysLinux, "mlockall() failed");
}
else {
DEBUG_LOG(LOGF_SysLinux, "Successfully locked into RAM");
}
}
}
}
#endif /* HAVE_MLOCKALL */
/* ================================================== */
int
SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
{
@@ -661,3 +632,265 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
return kernelvercmp(req_major, req_minor, 0, major, minor, patch) <= 0;
}
/* ================================================== */
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#define PHC_READINGS 10
static int
process_phc_readings(struct timespec ts[][3], int n, double precision,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
{
double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
int i, combined;
if (n > PTP_MAX_SAMPLES)
return 0;
for (i = 0; i < n; i++) {
delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
if (delays[i] < 0.0) {
/* Step in the middle of a PHC reading? */
DEBUG_LOG("Bad PTP_SYS_OFFSET sample delay=%e", delays[i]);
return 0;
}
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
sys_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
if (delays[i] > min_delay + MAX(sys_prec, precision))
continue;
phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
combined++;
}
assert(combined);
UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
*err = MAX(min_delay / 2.0, precision);
return 1;
}
/* ================================================== */
static int
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
struct timespec ts[PHC_READINGS][3];
struct ptp_sys_offset sys_off;
int i;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
return 0;
}
for (i = 0; i < PHC_READINGS; i++) {
ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
ts[i][1].tv_nsec = sys_off.ts[i * 2 + 1].nsec;
ts[i][2].tv_sec = sys_off.ts[i * 2 + 2].sec;
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
}
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
}
/* ================================================== */
static int
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
#ifdef PTP_SYS_OFFSET_EXTENDED
struct timespec ts[PHC_READINGS][3];
struct ptp_sys_offset_extended sys_off;
int i;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS;
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
return 0;
}
for (i = 0; i < PHC_READINGS; i++) {
ts[i][0].tv_sec = sys_off.ts[i][0].sec;
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
ts[i][1].tv_nsec = sys_off.ts[i][1].nsec;
ts[i][2].tv_sec = sys_off.ts[i][2].sec;
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
}
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
#else
return 0;
#endif
}
/* ================================================== */
static int
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
#ifdef PTP_SYS_OFFSET_PRECISE
struct ptp_sys_offset_precise sys_off;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
if (ioctl(phc_fd, PTP_SYS_OFFSET_PRECISE, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_PRECISE",
strerror(errno));
return 0;
}
phc_ts->tv_sec = sys_off.device.sec;
phc_ts->tv_nsec = sys_off.device.nsec;
sys_ts->tv_sec = sys_off.sys_realtime.sec;
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
return 1;
#else
return 0;
#endif
}
/* ================================================== */
int
SYS_Linux_OpenPHC(const char *path, int phc_index)
{
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;
}
/* ================================================== */
int
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
{
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 2;
return 1;
} else if ((*reading_mode == 3 || !*reading_mode) &&
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 3;
return 1;
} else if ((*reading_mode == 1 || !*reading_mode) &&
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 1;
return 1;
}
return 0;
}
/* ================================================== */
int
SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
int rising, int falling, int enable)
{
struct ptp_extts_request extts_req;
#ifdef PTP_PIN_SETFUNC
struct ptp_pin_desc pin_desc;
memset(&pin_desc, 0, sizeof (pin_desc));
pin_desc.index = pin;
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
pin_desc.chan = channel;
if (ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
return 0;
}
#else
DEBUG_LOG("Missing PTP_PIN_SETFUNC");
return 0;
#endif
memset(&extts_req, 0, sizeof (extts_req));
extts_req.index = channel;
extts_req.flags = (enable ? PTP_ENABLE_FEATURE : 0) |
(rising ? PTP_RISING_EDGE : 0) |
(falling ? PTP_FALLING_EDGE : 0);
if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_EXTTS_REQUEST", strerror(errno));
return 0;
}
return 1;
}
/* ================================================== */
int
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
{
struct ptp_extts_event extts_event;
if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
DEBUG_LOG("Could not read PHC extts event");
return 0;
}
phc_ts->tv_sec = extts_event.t.sec;
phc_ts->tv_nsec = extts_event.t.nsec;
*channel = extts_event.index;
return 1;
}
#endif

View File

@@ -31,14 +31,20 @@ extern void SYS_Linux_Initialise(void);
extern void SYS_Linux_Finalise(void);
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid);
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control);
extern void SYS_Linux_EnableSystemCallFilter(int level);
extern void SYS_Linux_MemLockAll(int LockAll);
extern void SYS_Linux_SetScheduler(int SchedPriority);
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
extern int SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err);
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
int rising, int falling, int enable);
extern int SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel);
#endif /* GOT_SYS_LINUX_H */

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2001
* Copyright (C) J. Hannken-Illjes 2001
* Copyright (C) Bryan Christianson 2015
* Copyright (C) Bryan Christianson 2015, 2017
*
* 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
@@ -46,6 +46,13 @@
#include "privops.h"
#include "util.h"
#ifdef HAVE_MACOS_SYS_TIMEX
#include <dlfcn.h>
#include "sys_netbsd.h"
static int have_ntp_adjtime = 0;
#endif
/* ================================================== */
/* This register contains the number of seconds by which the local
@@ -116,7 +123,7 @@ clock_initialise(void)
newadj.tv_usec = 0;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
}
@@ -154,10 +161,9 @@ start_adjust(void)
predicted_error = (current_drift_removal_interval - drift_removal_elapsed) / 2.0 * current_freq;
DEBUG_LOG(LOGF_SysMacOSX, "drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
1.0e6 * drift_removal_elapsed,
1.0e6 * current_drift_removal_interval,
1.0e6 * predicted_error);
DEBUG_LOG("drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
1.0e6 * drift_removal_elapsed, 1.0e6 * current_drift_removal_interval,
1.0e6 * predicted_error);
adjust_required = - (accrued_error + offset_register + predicted_error);
@@ -166,7 +172,7 @@ start_adjust(void)
rounding_error = adjust_required - adjustment_requested;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
old_adjust_remaining = UTI_TimevalToDouble(&oldadj);
@@ -190,7 +196,7 @@ stop_adjust(void)
zeroadj.tv_usec = 0;
if (PRV_AdjustTime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
LCL_ReadRawTime(&T1);
@@ -239,7 +245,7 @@ apply_step_offset(double offset)
UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
DEBUG_LOG("settimeofday() failed");
return 0;
}
@@ -338,14 +344,14 @@ set_sync_status(int synchronised, double est_error, double max_error)
/* update the RTC by applying a step of 0.0 secs */
apply_step_offset(0.0);
last_rtc_sync = now;
DEBUG_LOG(LOGF_SysMacOSX, "rtc synchronised");
DEBUG_LOG("rtc synchronised");
}
}
interval = ERROR_WEIGHT * est_error / (fabs(current_freq) + FREQUENCY_RES);
drift_removal_interval = MAX(interval, DRIFT_REMOVAL_INTERVAL_MIN);
DEBUG_LOG(LOGF_SysMacOSX, "est_error: %.3f current_freq: %.3f est drift_removal_interval: %.3f act drift_removal_interval: %.3f",
DEBUG_LOG("est_error: %.3f current_freq: %.3f est drift_removal_interval: %.3f act drift_removal_interval: %.3f",
est_error * 1.0e6, current_freq * 1.0e6, interval, drift_removal_interval);
}
@@ -389,7 +395,7 @@ set_realtime(void)
THREAD_TIME_CONSTRAINT_POLICY_COUNT);
if (kr != KERN_SUCCESS) {
LOG(LOGS_WARN, LOGF_SysMacOSX, "Cannot set real-time priority: %d", kr);
LOG(LOGS_WARN, "Cannot set real-time priority: %d", kr);
return -1;
}
return 0;
@@ -418,8 +424,8 @@ void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid)
/* ================================================== */
void
SYS_MacOSX_Initialise(void)
static void
legacy_MacOSX_Initialise(void)
{
clock_initialise();
@@ -435,8 +441,8 @@ SYS_MacOSX_Initialise(void)
/* ================================================== */
void
SYS_MacOSX_Finalise(void)
static void
legacy_MacOSX_Finalise(void)
{
SCH_RemoveTimeout(drift_removal_id);
@@ -445,4 +451,31 @@ SYS_MacOSX_Finalise(void)
/* ================================================== */
void
SYS_MacOSX_Initialise(void)
{
#ifdef HAVE_MACOS_SYS_TIMEX
have_ntp_adjtime = (dlsym(RTLD_NEXT, "ntp_adjtime") != NULL);
if (have_ntp_adjtime) {
SYS_NetBSD_Initialise();
return;
}
#endif
legacy_MacOSX_Initialise();
}
/* ================================================== */
void
SYS_MacOSX_Finalise(void)
{
#ifdef HAVE_MACOS_SYS_TIMEX
if (have_ntp_adjtime) {
SYS_NetBSD_Finalise();
return;
}
#endif
legacy_MacOSX_Finalise();
}
#endif

View File

@@ -65,14 +65,14 @@ accrue_offset(double offset, double corr_rate)
UTI_DoubleToTimeval(-offset, &newadj);
if (PRV_AdjustTime(&newadj, &oldadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
LOG_FATAL("adjtime() failed");
/* Add the old remaining adjustment if not zero */
doldadj = UTI_TimevalToDouble(&oldadj);
if (doldadj != 0.0) {
UTI_DoubleToTimeval(-offset + doldadj, &newadj);
if (PRV_AdjustTime(&newadj, NULL) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
}
@@ -84,9 +84,18 @@ get_offset_correction(struct timespec *raw,
{
struct timeval remadj;
double adjustment_remaining;
#ifdef MACOSX
struct timeval tv = {0, 0};
if (PRV_AdjustTime(&tv, &remadj) < 0)
LOG_FATAL("adjtime() failed");
if (PRV_AdjustTime(&remadj, NULL) < 0)
LOG_FATAL("adjtime() failed");
#else
if (PRV_AdjustTime(NULL, &remadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
LOG_FATAL("adjtime() failed");
#endif
adjustment_remaining = UTI_TimevalToDouble(&remadj);
@@ -138,7 +147,7 @@ SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
/* Check if we have write access to /dev/clockctl */
fd = open("/dev/clockctl", O_WRONLY);
if (fd < 0)
LOG_FATAL(LOGF_SysNetBSD, "Can't write to /dev/clockctl");
LOG_FATAL("Can't write to /dev/clockctl");
close(fd);
#endif
}

140
sys_null.c Normal file
View File

@@ -0,0 +1,140 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2017
*
* 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.
*
**********************************************************************
=======================================================================
Null clock driver for operation with no clock control.
*/
#include "config.h"
#include "sysincl.h"
#include "sys_null.h"
#include "local.h"
#include "localp.h"
#include "logging.h"
#include "util.h"
/* Current frequency offset of the system clock (in ppm) */
static double freq;
/* Offset of the system clock at the last update */
static double offset_register;
/* Time of the last update */
static struct timespec last_update;
/* Minimum interval between updates when frequency is constant */
#define MIN_UPDATE_INTERVAL 1000.0
/* ================================================== */
static void
update_offset(void)
{
struct timespec now;
double duration;
LCL_ReadRawTime(&now);
duration = UTI_DiffTimespecsToDouble(&now, &last_update);
offset_register += 1.0e-6 * freq * duration;
last_update = now;
DEBUG_LOG("System clock offset=%e freq=%f", offset_register, freq);
}
/* ================================================== */
static double
read_frequency(void)
{
return freq;
}
/* ================================================== */
static double
set_frequency(double freq_ppm)
{
update_offset();
freq = freq_ppm;
return freq;
}
/* ================================================== */
static void
accrue_offset(double offset, double corr_rate)
{
offset_register += offset;
}
/* ================================================== */
static int
apply_step_offset(double offset)
{
return 0;
}
/* ================================================== */
static void
offset_convert(struct timespec *raw, double *corr, double *err)
{
double duration;
duration = UTI_DiffTimespecsToDouble(raw, &last_update);
if (duration > MIN_UPDATE_INTERVAL) {
update_offset();
duration = 0.0;
}
*corr = -1.0e-6 * freq * duration - offset_register;
if (err)
*err = 0.0;
}
/* ================================================== */
void
SYS_Null_Initialise(void)
{
offset_register = 0.0;
LCL_ReadRawTime(&last_update);
lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset,
apply_step_offset, offset_convert, NULL, NULL);
LOG(LOGS_INFO, "Disabled control of system clock");
}
/* ================================================== */
void
SYS_Null_Finalise(void)
{
}

34
sys_null.h Normal file
View File

@@ -0,0 +1,34 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for null clock driver
*/
#ifndef GOT_SYS_NULL_H
#define GOT_SYS_NULL_H
extern void SYS_Null_Initialise(void);
extern void SYS_Null_Finalise(void);
#endif

109
sys_posix.c Normal file
View File

@@ -0,0 +1,109 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
*
* 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 is for POSIX compliant operating systems.
*/
#include "config.h"
#include "sysincl.h"
#include <sys/utsname.h>
#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
#include <pthread.h>
#include <sched.h>
#endif
#if defined(HAVE_MLOCKALL)
#include <sys/mman.h>
#endif
#if defined(HAVE_SETRLIMIT_MEMLOCK)
#include <sys/resource.h>
#endif
#include "sys_posix.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "util.h"
/* ================================================== */
#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
/* Install SCHED_FIFO real-time scheduler with specified priority */
void
SYS_Posix_SetScheduler(int priority)
{
struct sched_param sched;
int pmax, pmin;
if (priority < 1 || priority > 99)
LOG_FATAL("Bad scheduler priority: %d", priority);
sched.sched_priority = priority;
pmax = sched_get_priority_max(SCHED_FIFO);
pmin = sched_get_priority_min(SCHED_FIFO);
if (priority > pmax) {
sched.sched_priority = pmax;
} else if (priority < pmin) {
sched.sched_priority = pmin;
}
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched) < 0) {
LOG(LOGS_ERR, "pthread_setschedparam() failed");
} else {
DEBUG_LOG("Enabled SCHED_FIFO with priority %d", sched.sched_priority);
}
}
#endif /* HAVE_PTHREAD_SETSCHEDPARAM */
/* ================================================== */
#if defined(HAVE_MLOCKALL)
/* Lock the process into RAM so that it will never be swapped out */
void
SYS_Posix_MemLockAll(void)
{
#if defined(HAVE_SETRLIMIT_MEMLOCK)
struct rlimit rlim;
/* Ensure we can reserve as much as we need */
rlim.rlim_max = RLIM_INFINITY;
rlim.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
LOG(LOGS_ERR, "setrlimit() failed");
return;
}
#endif
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
LOG(LOGS_ERR, "mlockall() failed");
} else {
DEBUG_LOG("Successfully locked into RAM");
}
}
#endif /* HAVE_MLOCKALL */

36
sys_posix.h Normal file
View File

@@ -0,0 +1,36 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
*
* 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.
*
**********************************************************************
=======================================================================
The header file for shared Posix functionality
*/
#ifndef GOT_SYS_POSIX_H
#define GOT_SYS_POSIX_H
extern void SYS_Posix_MemLockAll(void);
extern void SYS_Posix_SetScheduler(int priority);
#endif /* GOT_SYS_POSIX_H */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015, 2017
*
* 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,7 +61,10 @@
#define MIN_TICK_RATE 100
/* Saved timex status */
static int status;
static int sys_status;
/* Saved TAI-UTC offset */
static int sys_tai_offset;
/* ================================================== */
@@ -95,33 +98,47 @@ set_frequency(double freq_ppm)
/* ================================================== */
static void
set_leap(int leap)
set_leap(int leap, int tai_offset)
{
struct timex txc;
int applied;
int applied, prev_status;
applied = 0;
if (!leap) {
txc.modes = 0;
if (SYS_Timex_Adjust(&txc, 1) == TIME_WAIT)
applied = 1;
}
txc.modes = 0;
applied = SYS_Timex_Adjust(&txc, 0) == TIME_WAIT;
status &= ~(STA_INS | STA_DEL);
prev_status = sys_status;
sys_status &= ~(STA_INS | STA_DEL);
if (leap > 0)
status |= STA_INS;
sys_status |= STA_INS;
else if (leap < 0)
status |= STA_DEL;
sys_status |= STA_DEL;
txc.modes = MOD_STATUS;
txc.status = status;
txc.status = sys_status;
#ifdef MOD_TAI
if (tai_offset) {
txc.modes |= MOD_TAI;
txc.constant = tai_offset;
if (applied && !(sys_status & (STA_INS | STA_DEL)))
sys_tai_offset += prev_status & STA_INS ? 1 : -1;
if (sys_tai_offset != tai_offset) {
sys_tai_offset = tai_offset;
LOG(LOGS_INFO, "System clock TAI offset set to %d seconds", tai_offset);
}
}
#endif
SYS_Timex_Adjust(&txc, 0);
LOG(LOGS_INFO, LOGF_SysTimex, "System clock status %s leap second",
leap ? (leap > 0 ? "set to insert" : "set to delete") :
(applied ? "reset after" : "set to not insert/delete"));
if (prev_status != sys_status) {
LOG(LOGS_INFO, "System clock status %s leap second",
leap ? (leap > 0 ? "set to insert" : "set to delete") :
(applied ? "reset after" : "set to not insert/delete"));
}
}
/* ================================================== */
@@ -149,12 +166,12 @@ set_sync_status(int synchronised, double est_error, double max_error)
#endif
if (synchronised)
status &= ~STA_UNSYNC;
sys_status &= ~STA_UNSYNC;
else
status |= STA_UNSYNC;
sys_status |= STA_UNSYNC;
txc.modes = MOD_STATUS | MOD_ESTERROR | MOD_MAXERROR;
txc.status = status;
txc.status = sys_status;
txc.esterror = est_error * 1.0e6;
txc.maxerror = max_error * 1.0e6;
@@ -169,17 +186,18 @@ initialise_timex(void)
{
struct timex txc;
status = STA_UNSYNC;
sys_status = STA_UNSYNC;
sys_tai_offset = 0;
/* Reset PLL offset */
txc.modes = MOD_OFFSET | MOD_STATUS;
txc.status = STA_PLL | status;
txc.status = STA_PLL | sys_status;
txc.offset = 0;
SYS_Timex_Adjust(&txc, 0);
/* Turn PLL off */
txc.modes = MOD_STATUS;
txc.status = status;
txc.status = sys_status;
SYS_Timex_Adjust(&txc, 0);
}
@@ -239,11 +257,9 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
if (state < 0) {
if (!ignore_error)
LOG_FATAL(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
txc->modes, strerror(errno));
LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
else
DEBUG_LOG(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
txc->modes, strerror(errno));
DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
}
return state;

View File

@@ -36,11 +36,11 @@
#include <float.h>
#include <glob.h>
#include <grp.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pwd.h>
#include <resolv.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
@@ -59,21 +59,17 @@
#include <time.h>
#include <unistd.h>
#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS)
#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS) || defined(HAVE_MACOS_SYS_TIMEX)
#include <sys/timex.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#elif HAVE_STDINT_H
#include <stdint.h>
#else
/* Tough */
#endif
#ifdef FEAT_IPV6
/* For inet_ntop() */
#include <arpa/inet.h>
#endif
#ifdef HAVE_GETRANDOM
#include <sys/random.h>
#endif
#endif /* GOT_SYSINCL_H */

View File

@@ -92,7 +92,7 @@ read_timeout(void *arg)
if (fabs(comp) <= MAX_COMP) {
comp = LCL_SetTempComp(comp);
DEBUG_LOG(LOGF_TempComp, "tempcomp updated to %f for %f", comp, temp);
DEBUG_LOG("tempcomp updated to %f for %f", comp, temp);
if (logfileid != -1) {
struct timespec now;
@@ -102,13 +102,11 @@ read_timeout(void *arg)
UTI_TimeToLogForm(now.tv_sec), temp, comp);
}
} else {
LOG(LOGS_WARN, LOGF_TempComp,
"Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
LOG(LOGS_WARN, "Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
comp, MAX_COMP);
}
} else {
LOG(LOGS_WARN, LOGF_TempComp, "Could not read temperature from %s",
filename);
LOG(LOGS_WARN, "Could not read temperature from %s", filename);
}
if (f)
@@ -126,7 +124,7 @@ read_points(const char *filename)
f = fopen(filename, "r");
if (!f) {
LOG_FATAL(LOGF_TempComp, "Could not open tempcomp point file %s", filename);
LOG_FATAL("Could not open tempcomp point file %s", filename);
return;
}
@@ -135,7 +133,7 @@ read_points(const char *filename)
while (fgets(line, sizeof (line), f)) {
p = (struct Point *)ARR_GetNewElement(points);
if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) {
LOG_FATAL(LOGF_TempComp, "Could not read tempcomp point from %s", filename);
LOG_FATAL("Could not read tempcomp point from %s", filename);
break;
}
}
@@ -143,7 +141,7 @@ read_points(const char *filename)
fclose(f);
if (ARR_GetSize(points) < 2)
LOG_FATAL(LOGF_TempComp, "Not enough points in %s", filename);
LOG_FATAL("Not enough points in %s", filename);
}
void

View File

@@ -4,6 +4,8 @@
cd ../..
export CFLAGS="-O2 -Werror -Wpointer-arith -Wformat-signedness -Wno-unknown-warning-option -D_FORTIFY_SOURCE=2"
for opts in \
"--enable-debug" \
"--enable-ntp-signd" \
@@ -23,6 +25,6 @@ for opts in \
"--disable-cmdmon --disable-refclock" \
"--disable-cmdmon --disable-ntp --disable-refclock"
do
./configure $opts
./configure $opts || exit 1
make "$@" || exit 1
done

View File

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

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