Compare commits

...

198 Commits

Author SHA1 Message Date
Miroslav Lichvar
16519ee2cc doc: update NEWS 2016-12-15 13:47:41 +01:00
Miroslav Lichvar
50022e9286 clientlog: enable NTP response rate limiting by default
Change the default interval of both NTP and command rate limiting to -10
(1024 packets per second) and the burst to 16. The default NTP leak is 2
(rate limiting is enabled by default) and the default command leak is 0
(rate limiting is disabled by default).
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
5059019535 clientlog: randomize alignment of log timestamps 2016-12-15 13:47:41 +01:00
Miroslav Lichvar
c6a38f5069 clientlog: allow very short rate limiting intervals
Support negative token shift to allow coarse rate limiting with
intervals down to -19.
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
11ed197663 configure: don't use recvmmsg() on FreeBSD
Don't try recvmmsg() on FreeBSD, at least for now. It is broken on
FreeBSD 11.0 and it's just a wrapper around recvmsg().
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
5634e6b963 doc: improve hwtimestamp description 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
db312a5ff6 ntp: allow wildcard in hwtimestamp directive
If "*" was specified, use getifaddrs() to get a list of all interfaces,
and try to enable HW timestamping on all of them.
2016-12-14 16:19:35 +01:00
Miroslav Lichvar
88c31b3785 client: improve ntpdata output 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
967f3e4f77 client: don't require address in ntpdata command
If no address is specified, use the SOURCE_DATA command to get addresses
of NTP sources, and request NTP_DATA for all of them.
2016-12-14 16:19:35 +01:00
Miroslav Lichvar
2e311d1766 sourcestats: add upper bound for skew 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
11f7cc0507 examples: avoid Unix domain socket in chrony-wait service
Use the -h option to force chronyc to use internet socket instead of
Unix domain as the access to the socket may be blocked by SELinux and
trying to open it generates SELinux warnings.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
a4f28892a5 cmdmon: update protocol changelog 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
5bc53741be sourcestats: add lower bound for std dev used for weighting 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
95a4f33265 sourcestats: save asymmetry run in dump files
This allows the asymmetry correction to be applied right after restart.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
fac1093ebf cmdmon: add reserved fields to ntpdata reply
This might be useful if ntpdata is changed to not require authorization
and new fields need to be added without breaking compatibility.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
1b1384ccaa nameserv: set CLOEXEC flag on pipe file descriptors 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
0c9a19ded5 stubs: rework emulation of asynchronous resolver to use pipes
With a larger number of configured servers, the handler of the emulated
resolver repeatedly scheduled timeout of zero, which triggered the
infinite loop detection in the scheduler and caused abort. This bug was
introduced in commit 967e358dbc.

Rework the code to use pipes instead of timeouts to avoid this problem.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
b7bd7469b7 ntp: disable maxdelayratio in interleaved/symmetric mode
It's too unreliable and the maxdelaydevratio test should work better
anyway.
2016-12-13 12:57:24 +01:00
Miroslav Lichvar
9568ff3f06 doc: update NEWS 2016-12-09 09:04:25 +01:00
Miroslav Lichvar
742ddcce11 doc: update README 2016-12-08 16:26:34 +01:00
Miroslav Lichvar
e72cc9e3da test: update 119-smoothtime 2016-12-08 16:25:46 +01:00
Lonnie Abelbeck
3156e5a293 client: add tab-completion with libedit/readline 2016-12-08 15:32:51 +01:00
Miroslav Lichvar
9a901e1cb0 refclock: make maximum lock age configurable
The maxlockage 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.
2016-12-08 14:47:38 +01:00
Miroslav Lichvar
8c11044ee2 refclock: slew last sample even after it was used
It may be needed by locked PPS refclocks.
2016-12-08 14:47:38 +01:00
Miroslav Lichvar
a75d2db75b test: add scan-build compilation test 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
6aac72fd80 configure: use common CPPFLAGS for all objects 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
b692cb720c configure: fix help text 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
25102489f5 ntp: fix clang warning 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
a2d2cad384 hwclock: fix check of sample separation 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
859e0c2323 ntp: add TX error to dispersion 2016-12-08 14:47:36 +01:00
Miroslav Lichvar
e62a39cafe ntp: fix RX error added to dispersion in interleaved mode 2016-12-08 14:47:33 +01:00
Miroslav Lichvar
8bbb8fa062 sources: add configurable limit for jitter
The maxjitter directive sets the maximum allowed jitter of the sources
to not be rejected by the source selection algorithm. This prevents
synchronisation with sources that have a small root distance, but their
time is too variable. By default, the maximum jitter is 1 second.
2016-12-08 14:20:00 +01:00
Miroslav Lichvar
68039e0d14 sourcestats: save variance as standard deviation
This reduces the number of sqrt() calls.
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
65fd30a547 cmdmon: allow all parameters to be set for new sources
Add missing fields to the REQ_NTP_Source structure and add new versions
of the ADD_SERVER/ADD_PEER commands.
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
23a4e8b38d ntp: rework calculation and testing of peer delay
Instead of a worst-case delay use a mean value and relate it to the
source's time. This makes it more stable in the interleaved and
symmetric modes, which should improve the weighting and asymmetry
correction. Modify the test A and B to work with a minimum estimated
delay (delay - dispersion).
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
979b53866d client: print addresses with refids in ntpdata report 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
46061d8eec client: fix truncation of long hostnames 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
946ee8f611 client: fix format specifier for poll in ntpdata report 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
7f757f09ce client: fix add command
The default version changed to 0 (autoselect).
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
9ba8a33966 sys_linux: allow openat in seccomp filter 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
492940568d main: add -t option to usage text 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
b95c2a3f78 configure: rename SOCKDIR to RUNDIR 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
53b661b59d regress: remove unused struct declaration 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
a049c9e0f8 conf: increase default minsamples and polltarget
Change default minsamples to 6 and polltarget to 8. This should improve
stability with extremely small jitters (e.g. HW timestamping) and not
decrease time accuracy at minimum polling interval too much.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
3513484852 main: add -t option to chronyd
This option sets a timeout (in seconds) after which chronyd will exit.
If the clock is not synchronised, it will exit with a non-zero status.
This is useful with the -q or -Q option to shorten the maximum time
waiting for measurements, or with the -r option to limit the time when
chronyd is running, but still allow it to adjust the frequency of the
system clock.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
2d67871bbf ntp: don't make client log entries for broadcast TX 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
e6e9a472db ntp: avoid truncation of NTPv4 MACs by default
If the MAC in NTPv4 requests would be truncated, use version 3 by
default to avoid the truncation. This is necessary for compatibility
with older chronyd servers, which do not respond to messages with
truncated MACs.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
1d5d768545 test: extend 105-ntpauth 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
6c8588c13c ntp: truncate MACs in NTPv4 packets
When sending an NTPv4 packet, truncate long MAC to 192 bits to follow
RFC 7822.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
89b127bf6c ntp: accept NTPv4 packets with truncated MACs
In order to allow deterministic parsing of NTPv4 extension fields, the
MAC must not be longer than 192 bits (RFC 7822). One way to get around
this limitation when using symmetric keys which produce longer MACs is
to truncate them to 192 bits (32-bit key ID and 160-bit hash).

Modify the code to accept NTPv4 packets with MACs truncated to 192
bits, but still allow long MACs in NTPv4 packets to not break
compatibility with older chrony clients.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
38c4a7ff97 keys: add support for checking truncated MACs 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
2f5b4aea91 util: move authentication and password decoding functions to keys
This doesn't need to be included in chronyc.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
4fc6a1b424 doc: update FAQ 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
6b3800cc94 doc: update man pages 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
633a007b7b doc: update README 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
756c2e9afb ntp: fix length modifier of refid in measurements log 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
27ea58d5fd client: zero pad reference ID 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
64f9205189 client: add ntpdata command 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
535ca64bba cmdmon: add ntpdata command 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
7255f9ef74 client: fix format specifiers in sourcestats report 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
cdb0b6124f client: add new format specifiers to print_report() 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
5fb1107cc7 client: print reference ID in hexadecimal
This is an incompatible change in the output of the tracking command,
which may break some scripts, but it's necessary to avoid confusion with
IPv4 addresses when synchronised to an IPv6 server or reference clock.
2016-11-25 17:33:43 +01:00
Miroslav Lichvar
1045adaa88 sources: give access to sourcestats instance
Give access to the sourcestats instance and remove all functions that
just translated to SST calls.
2016-11-25 17:33:43 +01:00
Miroslav Lichvar
ed286f3617 ntp: add new debug message 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
9f9dd7948b ntp: fix logging of RX timestamp source in interleaved mode 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
9c760de676 ntp: don't send presend packets in burst mode 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
90229984cf ntp: allow presend of zero
Don't use zero as a special value for disabled and change the default
presend to a value larger than any valid poll.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
2b3d64c31d ntp: send two presend packets in interleaved mode
In a burst of three requests (two presend + one normal) the server can
detect the client is using the interleaved mode and save the transmit
timestamp of the second response for the third response. This shortens
the interval in which the server has to keep the state.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
d23c647e34 ntp: shorten presend delay to 2 seconds 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
2408bbcd77 ntp: process presend responses
Rework the code to make a real request for presend and process the
response, but don't accumulate the sample. This allows presend to work
in the interleaved client mode.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
d75f6830f1 reference: randomize reference time
In unauthenticated interleaved symmetric NTP mode we should be now
careful with the reference timestamp as it may be useful with the peer
delay for estimating the local receive timestamp and increasing the
chance of spoofing a valid response from the peer.

When updating the reference time, add a random error of up to one second
to make it less sensitive when disclosed to NTP and cmdmon clients.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
4d7eb2f7a6 ntp: don't reset polling interval when switching to/from online
This allows chronyd to ramp up the polling interval even when the source
is frequently switched between the online and offline modes.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
3a67dedad6 ntp: fix calculation of PHC sample time 2016-11-23 10:08:36 +01:00
Miroslav Lichvar
518837e17a sys_linux: allow ioctls used with HW timestamping in seccomp filter 2016-11-23 09:24:05 +01:00
Miroslav Lichvar
c7e778757a ntp: transpose HW RX timestamps
We need to transpose HW RX timestamps as HW 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.
2016-11-22 16:15:35 +01:00
Miroslav Lichvar
c45be946ce Merge branch '2.4-stable' into HEAD 2016-11-22 16:06:05 +01:00
Miroslav Lichvar
258bcc21b8 refclock: don't compare sample time with samples from previous poll
This is an improvement of commit 8f85291d23.
2016-11-22 15:58:02 +01:00
Miroslav Lichvar
db286ca6ea doc: update NEWS 2016-11-21 12:03:45 +01:00
Miroslav Lichvar
85fbfd9b15 sources: add new status for sources that overlap trusted sources
Sources that overlap trusted sources should be displayed in the chronyc
sources report with the '-' symbol and they shouldn't trigger a
replacement.
2016-11-21 12:03:45 +01:00
Miroslav Lichvar
b819c7fe55 refclock: don't compare sample time with samples from previous poll
This is an improvement of commit 0a848e2528.
2016-11-21 12:03:27 +01:00
Miroslav Lichvar
2b5c86b9a3 refclock: fix check for old samples
The fix in commit 0a848e2528 was
incorrect.
2016-11-21 12:03:15 +01:00
Miroslav Lichvar
0a848e2528 refclock: require new samples to have newer timestamp
If all or most SHM/SOCK samples collected in a polling interval had the
same local timestamp, the dispersion could end up as nan, which could
trigger an assert failure later in the code.

Before accumulating a refclock sample, check if the timestamp is newer
than the previous one.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
b443ec5ea5 test: add smooth unit test 2016-11-21 12:02:51 +01:00
Miroslav Lichvar
37d1467368 smooth: fix selection of 1st stage direction
When the smoothing process is updated with extremely small (e.g.
sub-nanosecond) values, both directions may give a negative length of
the 1st or 3rd stage due to numerical errors and the selection will fail
an in assertion. Rework the code to select the direction which gives a
smaller error.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
1d9d19d76b client: flush stdout after printing prompt
Apparently fgets() doesn't flush stdout in some libc implementations.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
9603f0552a client: fix printing of negative poll in sources report again
This was broken in commit 3f51805e62.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
12befc2afd ntp: fix processing of kernel timestamps on non-Linux systems
When the SO_TIMESTAMP socket option was enabled, the expected type of
control messages containing timestamps was SO_TIMESTAMP instead of
SCM_TIMESTAMP. This worked on Linux, where the two values are equal, but
not on the other supported systems. The timestamps were ignored and this
probably worsened the accuracy and stability of the synchronisation.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
78f20f7b3e conf: fix parsing of refclock directive
Don't accept refclock directive which has as the last argument an option
that requires a value.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
875b0e262c ntp: add debug message for truncated control messages 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
8823e2b064 ntp: ignore truncated messages
Don't waste time with processing messages that don't fit in the receive
buffer as they most likely wouldn't pass the format check due to an
invalid length of an extension field.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
5b2caf48dc hwclock: fix order of samples
In order to trim oldest samples in the regression function, they need to
be sorted in the data arrays from the oldest to newest.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
7ec048ce7f ntp: detect unexpected TX updates of unknown sources 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
cfb3c3ba44 ntp: improve replay protection in symmetric mode
Always allow update from the first valid response, even if its transmit
timestamp is not newer than the currently saved timestamp. This shoud
provide a temporary protection in the case where the attacker does have
an authenticated packet from future, but the peers are using the same
polling interval and the protocol is already synchronised. This could be
also useful in the case where the attacker cannot observe the traffic
and authentication is disabled.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
4b0ef09221 sched: add more random bits to timeout scheduling
Extend the random value which is included in the calculation of the
delay from 16 to 32 bits. This makes scheduling of NTP transmissions
random to one microsecond for polling intervals up to 17.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
74f581e7ab client: randomize sequence number in requests
Don't rely on random source port of a connected socket alone as a
protection against spoofed packets in chronyc. Generate a fully random
32-bit sequence number for each request and modify the code to not send
a new request until the timeout expires or a valid response is received.
For a monitoring protocol this should be more than good enough.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
07aa54b183 client: fix attempt number in requests to be in network order 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
00da177e51 report: remove unused definition 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
6e9bfac07d sources: add new status for sources that overlap trusted sources
Sources that overlap trusted sources should be displayed in the chronyc
sources report with the '-' symbol and they shouldn't trigger a
replacement.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
06f93e7bf0 sources: don't log warning when opening dump file fails
Instead of complaining when the file doesn't exist, which is common when
using pool servers, log an informational message when the file is
loaded.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
d84a706c08 conf: create socket directory before logdir and dumpdir
This allows sharing of the same directory for sockets, logs and dumps as
the socket directory needs to be created first (with mode 0770) in order
to pass the check of the permissions.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
ea58a1e72c ntp: print offset and delay in debug messages in nanosecond resolution 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
5c691a5460 ntp: fix remote poll in measurements log
Write the poll value from the received packet instead of the saved
value, which doesn't have to be always updated.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
2c877fa149 ntp: add new fields to measurements log
Include reference ID, NTP mode and source of the local transmit and
receive timestamp in the measurements log.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
33053a5e14 ntp: add partial protection against replay attacks on symmetric mode
A recently published paper [1] (section VIII) describes a DoS attack
on symmetric associations authenticated with a symmetric key where the
attacker can only observe and replay packets. Although the attacker
cannot prevent packets from reaching the other peer (not even by
flooding the network for example), s/he has the same power as a MitM
attacker.

As the authors explain, this is a fundamental flaw of the protocol,
which cannot be fixed in the general case. However, we can at least try
to protect associations in a case where the peers use the same polling
interval (i.e. for each request is expected one response) and all peers
that share the symmetric key never start with clocks in future or very
distant past (i.e. the attacker does not have any packets from future
that could be replayed).

Require that updates of the NTP state between requests have increasing
transmit timestamp and when a packet that passed all NTP tests to be
considered a valid response was received, don't allow any more updates
of the state from packets that don't pass the tests. This should ensure
the last update of the state is from the first time the last real
response was received and still allow the protocol to recover in case
one of the peers steps its clock back or the attacker does have a packet
from future and the attack stops.

[1] Aanchal Malhotra, Matthew Van Gundy, Mayank Varia, Haydn Kennedy,
    Jonathan Gardner, and Sharon Goldberg. The Security of NTP's
    Datagram Protocol. https://eprint.iacr.org/2016/1006
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
8662652192 ntp: disable presend in symmetric and interleaved modes
The presend packet can't be used in symmetric and interleaved modes as
it breaks the protocol with unexpected packets.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
227c7e60a4 test: add util unit test 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
6e9c04896b util: add functions for zeroing and comparing NTP timestamps 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
0e273939d2 ntp: fix poll value in broadcast mode packets
Set poll in broadcast mode packets to the rounded log2 value of the
actual interval instead of a hardcoded value.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14647032b2 doc: update chrony.conf man page for recent changes 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14a1059e43 ntp: add support for HW timestamping on Linux
Add a new directive to specify interfaces which should be used for HW
timestamping. Extend the Linux ntp_io initialization to enable HW
timestamping, configure the RX filter using the SIOCSHWTSTAMP ioctl,
open their PHC devices, and track them as hwclock instances. When
messages with HW timestamps are received, use the PTP_SYS_OFFSET ioctl
to make PHC samples for hwclock.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
4449259d88 ntp: read interface index from control messages 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
01e5ea7d31 test: add 122-xleave 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
94522bfed1 test: add hwclock unit test 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
9bdd35c9fa hwclock: add support for tracking hardware clocks
Add a general support for tracking independent hardware clocks like PTP
hardware clocks (PHC) or real-time clocks (RTC).
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
d366530699 clientlog: move status check to get_record() 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
96d652e5bd ntp: add support for interleaved client/server mode
Adapt the interleaved symmetric mode for client/server associations.
On server, save the state needed for detection and responding in the
interleaved mode in the client log. On client, enable the interleaved
mode when the server is specified with the xleave option. Always accept
responses in basic mode to allow synchronization with servers that
don't support the interleaved mode, have too many clients, or have
multiple clients behing the same IP address. This is also necessary to
prevent DoS attacks on the client by overwriting or flushing the server
state. Protect the client's state variables against replay attacks as
the timestamps are now needed when processing the subsequent packet.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
bd736f9234 ntp: check also NTP receive timestamp when updating TX timestamp 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
90b25f5b83 ntp: add support for interleaved symmetric mode
Add xleave option to the peer directive to enable an interleaved mode
compatible with ntpd. This allows peers to exchange transmit timestamps
captured after the actual transmission and significantly improve
the accuracy of the measurements.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
997406fe47 ntp: add support for software timestamping on Linux
Enable SCM_TIMESTAMPING control messages and the socket's error queue in
order to receive our transmitted packets with a more accurate transmit
timestamp. Add a new file for Linux-specific NTP I/O and implement
processing of these messages there.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14c8f07629 ntp: save source of local timestamps
Introduce a new structure for local timestamps that will hold the
timestamp with its estimated error and also its source (daemon, kernel
or HW). While at it, reorder parameters of the functions that accept the
timestamps.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
8f6a1b5318 ntp: add support for processing of transmitted packets
Add new functions for processing of packets after they are actually
sent by the kernel or HW in order to get a more accurate transmit
timestamp. Rename old functions for processing of received packets and
their parameters to make the naming more consistent.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
a8c6bea2d5 sys_linux: add function for checking kernel version 2016-11-10 15:26:55 +01:00
Miroslav Lichvar
19fde8f49c refclock: fix check for old samples
The fix in commit 8f85291d23 was
incorrect.
2016-10-07 11:03:59 +02:00
Miroslav Lichvar
8f85291d23 refclock: require new samples to have newer timestamp
If all or most SHM/SOCK samples collected in a polling interval had the
same local timestamp, the dispersion could end up as nan, which could
trigger an assert failure later in the code.

Before accumulating a refclock sample, check if the timestamp is newer
than the previous one.
2016-10-06 16:09:24 +02:00
Miroslav Lichvar
9c48166e90 ntp: inline send_packet()
Also, reuse existing function for checking server sockets.
2016-09-26 12:40:44 +02:00
Miroslav Lichvar
b536296c05 ntp: use ipi_addr from struct in_pktinfo as local address
Use the ipi_addr field instead of ipi_spec_dst as the local address
after recvmsg() to be consistent with the processing of struct
in6_pktinfo. This may make a difference for messages from the error
queue.
2016-09-26 12:40:44 +02:00
Miroslav Lichvar
d36c522453 ntp: check for missing source address after recvmsg() 2016-09-26 12:40:44 +02:00
Miroslav Lichvar
2577e20f09 ntp: fix updating of transmit delay in symmetric mode
This was broken in commit cea21adbbb.
2016-09-26 12:40:43 +02:00
Miroslav Lichvar
c169ad3f58 sched: add support for handling exceptions on descriptors 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
411f4697ca sys_linux: allow getdents in seccomp filter
This is needed for glob(), which is used with the include and dumpdir
directives.
2016-09-26 12:40:43 +02:00
Miroslav Lichvar
6c5de8dcb0 refclock: use UTI_TimespecToString() in debug message 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
c8373f1649 util: add UTI_IsZeroTimespec() 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
45f86122fa test: add smooth unit test 2016-09-12 13:13:30 +02:00
Miroslav Lichvar
c0a8afdb68 smooth: fix selection of 1st stage direction
When the smoothing process is updated with extremely small (e.g.
sub-nanosecond) values, both directions may give a negative length of
the 1st or 3rd stage due to numerical errors and the selection will fail
an in assertion. Rework the code to select the direction which gives a
smaller error.
2016-09-12 13:13:14 +02:00
Miroslav Lichvar
1afb285aad sched: initialize sub-second part of saved_tv in SCH_MainLoop()
This is needed since commit d0dfa1de9e to
avoid valgrind errors.
2016-09-12 12:49:18 +02:00
Miroslav Lichvar
c08e7e716d use correct facility in LOG messages 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
a06a5f1baa sources: remove dump files on start
When chronyd is starting, after the point where dump files are loaded,
remove all files in the dump directory that match the naming scheme used
for dump files. This prevents loading stale dump files that were not
saved in the latest run of chronyd.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
fb5d4f1da4 conf: disable dumpdir and logdir by default
Use empty string instead of "." (which is normally the root directory)
as the default value of dumpdir and logdir to indicate they are not
specified. Print warnings in syslog when trying to log or dump
measurements without dumpdir or logdir.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
d2e5b41369 client: flush stdout after printing prompt
Apparently fgets() doesn't flush stdout in some libc implementations.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
4b6b6e5cba client: remove out of date comment 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
27b4c396d0 client: fix printing of negative poll in sources report again
This was broken in commit 3f51805e62.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
41eb5b79cb client: check address in waitsync command 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
23cf74d5c7 util: convert invalid addresses as IPADDR_UNSPEC 2016-09-07 11:15:57 +02:00
Miroslav Lichvar
1a038bfd50 test: add 011-asymjitter 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
dd02d67224 test: add support for testing with asymmetric jitter 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
648bf8bd3e test: extend 113-leapsecond 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
82c4bfe5d2 sources: include trust option in leap second voting
When sources specified with the trust option pass the source selection,
ignore other sources in the vote of leap second status.
2016-09-06 15:48:59 +02:00
Miroslav Lichvar
98ba4ce4d5 configure: add options to set default pidfile and rtcdevice 2016-08-22 15:50:35 +02:00
Bryan Christianson
f63e414024 configure: add option --without-clock-gettime
clock_gettime() will be ignored even if it is present
2016-08-22 15:50:35 +02:00
Miroslav Lichvar
a8886603c2 ntp: fix processing of kernel timestamps on non-Linux systems
When the SO_TIMESTAMP socket option was enabled, the expected type of
control messages containing timestamps was SO_TIMESTAMP instead of
SCM_TIMESTAMP. This worked on Linux, where the two values are equal, but
not on the other supported systems. The timestamps were ignored and this
probably worsened the accuracy and stability of the synchronisation.
2016-08-22 15:50:35 +02:00
Miroslav Lichvar
4f10144b09 ntp: add corrected delay to debug message in process_receive() 2016-08-22 15:50:28 +02:00
Miroslav Lichvar
af664e6cec sourcestats: return success when loading dump file with no samples 2016-08-22 15:05:48 +02:00
Miroslav Lichvar
c30816eb65 sourcestats: remove warning messages from SST_LoadFromFile()
There is a generic error message in SRC_ReloadSources().
2016-08-22 15:05:48 +02:00
Miroslav Lichvar
b1accfd0ff sourcestats: make reading/writing dump files Y2106 ready
The sample times were written and read as unsigned long, which would
overflow in year 2016 on platforms that have 32-bit long.
2016-08-22 15:05:48 +02:00
Miroslav Lichvar
5c45e4ccb5 sources: improve naming of dump files
Include IP address instead of reference ID in the name of dump file
for NTP sources and for reference clocks format the reference ID as a
hexadecimal number instead of quad dotted notation.

Also, avoid dynamic memory allocation and improve warning messages.
2016-08-22 15:05:02 +02:00
Miroslav Lichvar
41cf867738 sourcestats: update regression after loading dump file
Call SST_DoNewRegression() immediately in SST_LoadFromFile instead of
relying on SRC_ReloadSources().
2016-08-19 18:25:02 +02:00
Bryan Christianson
02844e9b01 local: fix typo in strerror() call 2016-08-19 18:25:02 +02:00
Miroslav Lichvar
7a1ebc3467 ntp: add support for SO_TIMESTAMPNS socket option
Enable the SO_TIMESTAMPNS option to get kernel timestamps in nanosecond
resolution.
2016-08-19 13:55:20 +02:00
Miroslav Lichvar
8d89610ff6 local: add support for clock_gettime()
Use clock_gettime() to read the system clock in nanosecond resolution.
2016-08-19 13:54:58 +02:00
Miroslav Lichvar
cfe706f032 util: modify UTI_*ToDouble functions to return double directly 2016-08-19 12:53:09 +02:00
Miroslav Lichvar
99cc94529d util: rename functions dealing with integers in NTP format
This should prevent confusion with int32_t, int64_t and other types.
2016-08-19 12:53:09 +02:00
Miroslav Lichvar
d0dfa1de9e adopt struct timespec
Replace struct timeval with struct timespec as the main data type for
timestamps. This will allow the NTP code to work with timestamps in
nanosecond resolution.
2016-08-19 12:53:09 +02:00
Miroslav Lichvar
0899ab52dd util: return normalised timevals 2016-08-19 11:33:38 +02:00
Miroslav Lichvar
71e0ebcb6b ntp: don't send crypto-NAKs
Crypto-NAK is useful only with Autokey where it allows quick reset
of the association. There is no plan to support Autokey and NTS will
specify its own message for authentication errors.
2016-08-17 11:54:34 +02:00
Miroslav Lichvar
e488371b01 sourcestats: report asymmetry in statistics log 2016-08-11 11:24:48 +02:00
Miroslav Lichvar
39f34eb674 sourcestats: correct offsets with asymmetric network jitter
Estimate asymmetry of network jitter on the path to the source as a
slope of offset against network delay in multiple linear regression. If
the asymmetry is significant and its sign doesn't change frequently, the
measured offsets (which are used later to estimate the offset and
frequency of the clock) are corrected to correspond to the minimum
network delay. This can significantly improve the accuracy and stability
of the estimated offset and frequency.
2016-08-11 11:24:48 +02:00
Miroslav Lichvar
9d9d6c30cf sourcestats: add debug message for regression results 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
27d59e54cc regress: add linear regression with two independent variables 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
507a01ab17 sourcestats: extend array holding peer delays
Keep the same number of peer delays as offsets. This will be needed when
peer delay is included in offset correction.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
f7b8cd1a09 regress: save arrays of constants in single-precision
No need to waste space on double precision. Also, declare them as const.
2016-08-11 10:45:48 +02:00
Bryan Christianson
8bc48af630 rename 'Mac OS X' to 'macOS'
From the the release of macOS Sierra (Version 10.12) the Macintosh
operating system is called 'macOS'
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
b0838280a9 ntp: reset tentative flag only when sample was accumulated
When selecting sources from a pool, ignore responses which didn't
produce a new sample. Sources with acceptable delay (as configured by
the maxdelay* options) should be prefered.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
cea21adbbb ntp: close client sockets sooner with unsynchronised sources
When a valid packet is received from an unsynchronised source (i.e. only
a test of leap, stratum or root distance failed), there is no point in
waiting for another packet or the RX timeout, and the client socket can
be immediately closed.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
c619d555f0 test: add 010-multrecv 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
e306199588 ntp: add support for recvmmsg()
This is used to read multiple packets with one system call. It should
work on Linux and NetBSD.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
895c15d677 configure: include config.h in test code 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
d18f9ca75a ntp: rework receiving messages
Allocate buffers for received messages on heap instead of stack and
prepare the code for receiving multiple messages at the same time.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
82e76c39d9 ntp: align buffers for control messages 2016-08-11 10:45:47 +02:00
Miroslav Lichvar
577aed4842 ntp: add support for MS-SNTP authentication in Samba
Add support for authenticating MS-SNTP responses in Samba (ntp_signd).
Supported is currently only the old MS-SNTP authenticator field. It's
disabled by default. It can be enabled with the --enable-ntp-signd
configure option and the ntpsigndsocket directive, which specifies the
location of the Samba ntp_signd socket.
2016-07-29 10:17:33 +02:00
Miroslav Lichvar
2a8ce63fc7 ntp: detect MS-SNTP packets
When a received packet fails to authenticate, check if the digest
contains zeroes and treat it as an MS-SNTP packet with authenticator or
extended authenticator field. For now, discard these packets, i.e. don't
respond with a crypto-NAK.
2016-07-29 10:17:33 +02:00
Miroslav Lichvar
61dd4e0ccb ntp: refactor selection of authentication mode
Replace the flag that enables authentication using a symmetric key with
an enum. Specify crypto-NAK as a special mode used for responses instead
of relying on zero key ID. Also, rework check_packet_auth() to always
save the mode and key ID.
2016-07-29 10:17:13 +02:00
Miroslav Lichvar
8220e51ae4 ntp: check for extension fields only in NTPv4 packets 2016-07-20 12:47:38 +02:00
Miroslav Lichvar
d322c8e6e5 doc: improve description of chronyc -n option 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
3dec266dd5 client: indicate truncated addresses/hostnames
Add symbol > to the end of the resolved hostname or address when it's
truncated.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
862938cc79 client: truncate long hostnames in clients output 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
ee396702f2 client: print intervals in seconds up to 1200 seconds
This covers random variations in the default maximum polling interval.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
316d50d6f1 sources: optimize SRC_ReportSource() a bit
Remove unnecessary memset() call and use the default case of the switch
to report the unreachable state.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
5e92aaf8a5 addressing: pad IPAddr struct explicitly 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
7ffe59a734 util: round up when converting to 32-bit NTP values
NTP clients shouldn't get root delay and dispersion smaller than the
server's values.
2016-07-20 09:17:24 +02:00
Miroslav Lichvar
6cd558398a ntp: add offset option
Add offset option to the server/pool/peer directive. It specifies a
correction which will be applied to offsets measured with the NTP
source. It's particularly useful to compensate for a known asymmetry in
network delay or timestamping errors.
2016-06-28 15:40:58 +02:00
Miroslav Lichvar
632cd1a177 client: rework error printing for unsupported source options 2016-06-28 13:26:09 +02:00
Miroslav Lichvar
223ad0e8aa conf: fix parsing of refclock directive
Don't accept refclock directive which has as the last argument an option
that requires a value.
2016-06-28 13:23:27 +02:00
Miroslav Lichvar
f8bd9ab378 cmdparse: remove CPS_Status
Remove command and option specific error codes. Return zero on any
failure.
2016-06-27 14:52:55 +02:00
Miroslav Lichvar
d78e8f096c cmdparse: refactor CPS_ParseNTPSourceAdd() 2016-06-27 14:52:49 +02:00
Miroslav Lichvar
57fc2ff1be sched: add support for output file event
This allows waiting for non-blocking write operations.
2016-06-23 11:45:49 +02:00
Miroslav Lichvar
d8d096aa54 sched: don't keep prepared fd_set
Instead of copying a prepared fd_set to the fd_set used by select(),
fill it from scratch according to the array of file handlers before each
select() call. This should make the code simpler and save some memory
when other events are supported.
2016-06-23 11:34:00 +02:00
Miroslav Lichvar
0a10545314 sched: rework file handling API
Replace SCH_*InputFileHandler() functions with more general
SCH_*FileHandler(), where events are specified as a new parameter and
which will later support other file events, e.g. file ready for ouput
and exception.

The file handlers have two new parameters: file descriptor and event.
2016-06-23 11:33:54 +02:00
Miroslav Lichvar
aeb57a36b2 logging: fix LOG_MESSAGE macro to not use semicolon 2016-06-16 17:15:36 +02:00
95 changed files with 5539 additions and 2151 deletions

View File

@@ -63,12 +63,6 @@ chronyd : $(OBJS) $(EXTRA_OBJS)
chronyc : $(CLI_OBJS) chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS) $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
client.o : client.c
$(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
$(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
$(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $<
distclean : clean distclean : clean
-rm -f .DS_Store -rm -f .DS_Store
-rm -f Makefile config.h config.log -rm -f Makefile config.h config.log

43
NEWS
View File

@@ -1,3 +1,46 @@
New in version 3.0
==================
Enhancements
------------
* Add support for software and hardware timestamping on Linux
* Add support for client/server and symmetric interleaved modes
* Add support for MS-SNTP authentication in Samba
* Add support for truncated MACs in NTPv4 packets
* Estimate and correct for asymmetric network jitter
* Increase default minsamples and polltarget to improve stability
with very low jitter
* Add maxjitter directive to limit source selection by jitter
* Add offset option to server/pool/peer directive
* Add maxlockage option to refclock directive
* Add -t option to chronyd to exit after specified time
* Add partial protection against replay attacks on symmetric mode
* Don't reset polling interval when switching sources to online state
* Enable NTP response rate limiting by default
(1024 packets per second per IP address and 25% leak)
* Improve maximum server throughput on Linux and NetBSD
* Remove dump files after start
* Add tab-completion to chronyc with libedit/readline
* Add ntpdata command to print details about NTP measurements
* Allow all source options to be set in add server/peer command
* Indicate truncated addresses/hostnames in chronyc output
* Print reference IDs as hexadecimal numbers to avoid confusion with
IPv4 addresses
Bug fixes
---------
* Fix crash with disabled asynchronous name resolving
New in version 2.4.1
====================
Bug fixes
---------
* Fix processing of kernel timestamps on non-Linux systems
* Fix crash with smoothtime directive
* Fix validation of refclock sample times
* Fix parsing of refclock directive
New in version 2.4 New in version 2.4
================== ==================

45
README
View File

@@ -14,10 +14,10 @@ intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature), temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine. and systems that do not run continuosly, or run on a virtual machine.
Typical accuracy between two machines on a LAN is in tens, or a few Typical accuracy between two machines synchronised over the Internet is
hundreds, of microseconds; over the Internet, accuracy is typically within a few milliseconds; on a LAN, accuracy is typically in tens of
within a few milliseconds. With a good hardware reference clock microseconds. With hardware timestamping or a hardware reference clock
sub-microsecond accuracy is possible. sub-microsecond accuracy may be possible.
Two programs are included in chrony, chronyd is a daemon that can be Two programs are included in chrony, chronyd is a daemon that can be
started at boot time and chronyc is a command-line interface program started at boot time and chronyc is a command-line interface program
@@ -27,7 +27,7 @@ operating parameters whilst it is running.
What will chrony run on? What will chrony run on?
======================== ========================
The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will Solaris. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one likely require a porting exercise. You would need to start from one
of the existing system-specific drivers and look into the quirks of of the existing system-specific drivers and look into the quirks of
@@ -95,19 +95,12 @@ License
chrony is distributed under the GNU General Public License version 2. chrony is distributed under the GNU General Public License version 2.
Authors
Author =======
======
Richard P. Curnow <rc@rc0.org.uk> Richard P. Curnow <rc@rc0.org.uk>
Maintainers
===========
Miroslav Lichvar <mlichvar@redhat.com> Miroslav Lichvar <mlichvar@redhat.com>
Acknowledgements Acknowledgements
================ ================
@@ -118,6 +111,9 @@ implementation has been used to check the details of the protocol.
The following people have provided patches and other major contributions The following people have provided patches and other major contributions
to the program : to the program :
Lonnie Abelbeck <lonnie@abelbeck.com>
Patch to add tab-completion to chronyc
Benny Lyne Amorsen <benny@amorsen.dk> Benny Lyne Amorsen <benny@amorsen.dk>
Patch to add minstratum option Patch to add minstratum option
@@ -139,7 +135,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Entries in contrib directory Entries in contrib directory
Bryan Christianson <bryan@whatroute.net> Bryan Christianson <bryan@whatroute.net>
Support for Mac OS X Support for macOS
Support for privilege separation Support for privilege separation
Entries in contrib directory Entries in contrib directory
@@ -198,18 +194,6 @@ Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi> Antti Jrvinen <costello@iki.fi>
Advice on configuring for BSD/386 Advice on configuring for BSD/386
Miroslav Lichvar <mlichvar@redhat.com>
Reference clock support
IPv6 support
Linux capabilities support
Leap second support
Improved source selection
Improved sample history trimming
Improved polling interval adjustment
Improved stability with temporary asymmetric delays
Temperature compensation
Many other bug fixes and improvements
Victor Moroz <vim@prv.adlum.ru> Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100 Patch to support Linux with HZ!=100
@@ -228,6 +212,9 @@ Andreas Piesk <apiesk@virbus.de>
Timo Teras <timo.teras@iki.fi> Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts Patch to reply correctly on multihomed hosts
Bill Unruh <unruh@physics.ubc.ca>
Advice on statistics
Stephen Wadeley <swadeley@redhat.com> Stephen Wadeley <swadeley@redhat.com>
Improvements to man pages Improvements to man pages
@@ -244,5 +231,5 @@ Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Doug Woodward <dougw@whistler.com> Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86 Advice on configuring for Solaris 2.8 on x86
Many other people have contributed bug reports and suggestions. I'm Many other people have contributed bug reports and suggestions. We are sorry
sorry I can't identify all of you individually. we cannot identify all of you individually.

View File

@@ -42,6 +42,7 @@ typedef struct {
uint8_t in6[16]; uint8_t in6[16];
} addr; } addr;
uint16_t family; uint16_t family;
uint16_t _pad;
} IPAddr; } IPAddr;
typedef struct { typedef struct {

75
candm.h
View File

@@ -94,14 +94,17 @@
#define REQ_SERVER_STATS 54 #define REQ_SERVER_STATS 54
#define REQ_CLIENT_ACCESSES_BY_INDEX2 55 #define REQ_CLIENT_ACCESSES_BY_INDEX2 55
#define REQ_LOCAL2 56 #define REQ_LOCAL2 56
#define N_REQUEST_TYPES 57 #define REQ_NTP_DATA 57
#define REQ_ADD_SERVER2 58
#define REQ_ADD_PEER2 59
#define N_REQUEST_TYPES 60
/* Structure used to exchange timevals independent on size of time_t */ /* Structure used to exchange timespecs independent of time_t size */
typedef struct { typedef struct {
uint32_t tv_sec_high; uint32_t tv_sec_high;
uint32_t tv_sec_low; uint32_t tv_sec_low;
uint32_t tv_nsec; uint32_t tv_nsec;
} Timeval; } Timespec;
/* This is used in tv_sec_high for 32-bit timestamps */ /* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff #define TV_NOHIGHSEC 0x7fffffff
@@ -200,12 +203,12 @@ typedef struct {
} REQ_Modify_Makestep; } REQ_Modify_Makestep;
typedef struct { typedef struct {
Timeval ts; Timespec ts;
int32_t EOR; int32_t EOR;
} REQ_Logon; } REQ_Logon;
typedef struct { typedef struct {
Timeval ts; Timespec ts;
int32_t EOR; int32_t EOR;
} REQ_Settime; } REQ_Settime;
@@ -246,6 +249,7 @@ typedef struct {
#define REQ_ADDSRC_NOSELECT 0x10 #define REQ_ADDSRC_NOSELECT 0x10
#define REQ_ADDSRC_TRUST 0x20 #define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40 #define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
@@ -253,9 +257,17 @@ typedef struct {
int32_t minpoll; int32_t minpoll;
int32_t maxpoll; int32_t maxpoll;
int32_t presend_minpoll; int32_t presend_minpoll;
uint32_t min_stratum;
uint32_t poll_target;
uint32_t version;
uint32_t max_sources;
int32_t min_samples;
int32_t max_samples;
uint32_t authkey; uint32_t authkey;
Float max_delay; Float max_delay;
Float max_delay_ratio; Float max_delay_ratio;
Float max_delay_dev_ratio;
Float offset;
uint32_t flags; uint32_t flags;
int32_t EOR; int32_t EOR;
} REQ_NTP_Source; } REQ_NTP_Source;
@@ -309,6 +321,11 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_SmoothTime; } REQ_SmoothTime;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_NTPData;
/* ================================================== */ /* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1 #define PKT_TYPE_CMD_REQUEST 1
@@ -345,8 +362,8 @@ typedef struct {
domain socket. domain socket.
Version 6 (no authentication) : changed format of client accesses by index Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types), new flags in NTP source request and report, (using new request/reply types), new fields and flags in NTP source request
new commands: refresh, serverstats and report, new commands: ntpdata, refresh, serverstats
*/ */
#define PROTO_VERSION_NUMBER 6 #define PROTO_VERSION_NUMBER 6
@@ -409,6 +426,7 @@ typedef struct {
REQ_ManualDelete manual_delete; REQ_ManualDelete manual_delete;
REQ_ReselectDistance reselect_distance; REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime; REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data;
} data; /* Command specific parameters */ } data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the /* Padding used to prevent traffic amplification. It only defines the
@@ -442,7 +460,8 @@ typedef struct {
#define RPY_SMOOTHING 13 #define RPY_SMOOTHING 13
#define RPY_SERVER_STATS 14 #define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15 #define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define N_REPLY_TYPES 16 #define RPY_NTP_DATA 16
#define N_REPLY_TYPES 17
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -512,7 +531,7 @@ typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
uint16_t stratum; uint16_t stratum;
uint16_t leap_status; uint16_t leap_status;
Timeval ref_time; Timespec ref_time;
Float current_correction; Float current_correction;
Float last_offset; Float last_offset;
Float rms_offset; Float rms_offset;
@@ -540,7 +559,7 @@ typedef struct {
} RPY_Sourcestats; } RPY_Sourcestats;
typedef struct { typedef struct {
Timeval ref_time; Timespec ref_time;
uint16_t n_samples; uint16_t n_samples;
uint16_t n_runs; uint16_t n_runs;
uint32_t span_seconds; uint32_t span_seconds;
@@ -590,7 +609,7 @@ typedef struct {
#define MAX_MANUAL_LIST_SAMPLES 16 #define MAX_MANUAL_LIST_SAMPLES 16
typedef struct { typedef struct {
Timeval when; Timespec when;
Float slewed_offset; Float slewed_offset;
Float orig_offset; Float orig_offset;
Float residual; Float residual;
@@ -624,6 +643,39 @@ typedef struct {
int32_t EOR; int32_t EOR;
} RPY_Smoothing; } RPY_Smoothing;
#define RPY_NTP_FLAGS_TESTS 0x3ff
#define RPY_NTP_FLAG_INTERLEAVED 0x4000
#define RPY_NTP_FLAG_AUTHENTICATED 0x8000
typedef struct {
IPAddr remote_addr;
IPAddr local_addr;
uint16_t remote_port;
uint8_t leap;
uint8_t version;
uint8_t mode;
uint8_t stratum;
int8_t poll;
int8_t precision;
Float root_delay;
Float root_dispersion;
uint32_t ref_id;
Timespec ref_time;
Float offset;
Float peer_delay;
Float peer_dispersion;
Float response_time;
Float jitter_asymmetry;
uint16_t flags;
uint8_t tx_tss_char;
uint8_t rx_tss_char;
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;
typedef struct { typedef struct {
uint8_t version; uint8_t version;
uint8_t pkt_type; uint8_t pkt_type;
@@ -652,6 +704,7 @@ typedef struct {
RPY_ManualList manual_list; RPY_ManualList manual_list;
RPY_Activity activity; RPY_Activity activity;
RPY_Smoothing smoothing; RPY_Smoothing smoothing;
RPY_NTPData ntp_data;
} data; /* Reply specific parameters */ } data; /* Reply specific parameters */
} CMD_Reply; } CMD_Reply;

488
client.c
View File

@@ -94,8 +94,11 @@ void LOG_Message(LOG_Severity severity,
} }
/* ================================================== */ /* ================================================== */
/* Read a single line of commands from standard input. Eventually we /* Read a single line of commands from standard input */
might want to use the GNU readline library. */
#ifdef FEAT_READLINE
static char **command_name_completion(const char *text, int start, int end);
#endif
static char * static char *
read_line(void) read_line(void)
@@ -107,6 +110,9 @@ read_line(void)
#ifdef FEAT_READLINE #ifdef FEAT_READLINE
char *cmd; char *cmd;
rl_attempted_completion_function = command_name_completion;
rl_basic_word_break_characters = "\t\n\r";
/* save line only if not empty */ /* save line only if not empty */
cmd = readline(prompt); cmd = readline(prompt);
if( cmd == NULL ) return( NULL ); if( cmd == NULL ) return( NULL );
@@ -125,6 +131,7 @@ read_line(void)
return( line ); return( line );
#else #else
printf("%s", prompt); printf("%s", prompt);
fflush(stdout);
#endif #endif
} }
if (fgets(line, sizeof(line), stdin)) { if (fgets(line, sizeof(line), stdin)) {
@@ -1058,51 +1065,24 @@ static int
process_cmd_add_server_or_peer(CMD_Request *msg, char *line) process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
{ {
CPS_NTP_Source data; CPS_NTP_Source data;
CPS_Status status;
IPAddr ip_addr; IPAddr ip_addr;
char str[64]; int result = 0, status;
int result = 0; const char *opt_name;
status = CPS_ParseNTPSourceAdd(line, &data); status = CPS_ParseNTPSourceAdd(line, &data);
switch (status) { switch (status) {
case CPS_Success: case 0:
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for add command");
break;
default:
if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) { if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Invalid host/IP address"); LOG(LOGS_ERR, LOGF_Client, "Invalid host/IP address");
break; break;
} }
if (data.params.min_stratum != SRC_DEFAULT_MINSTRATUM) { opt_name = NULL;
LOG(LOGS_WARN, LOGF_Client, "Option minstratum not supported"); if (opt_name) {
break; LOG(LOGS_ERR, LOGF_Client, "%s can't be set in chronyc", opt_name);
}
if (data.params.poll_target != SRC_DEFAULT_POLLTARGET) {
LOG(LOGS_WARN, LOGF_Client, "Option polltarget not supported");
break;
}
if (data.params.max_delay_dev_ratio != SRC_DEFAULT_MAXDELAYDEVRATIO) {
LOG(LOGS_WARN, LOGF_Client, "Option maxdelaydevratio not supported");
break;
}
if (data.params.version != NTP_VERSION) {
LOG(LOGS_WARN, LOGF_Client, "Option version not supported");
break;
}
if (data.params.max_sources != SRC_DEFAULT_MAXSOURCES) {
LOG(LOGS_WARN, LOGF_Client, "Option maxsources not supported");
break;
}
if (data.params.min_samples != SRC_DEFAULT_MINSAMPLES) {
LOG(LOGS_WARN, LOGF_Client, "Option minsamples not supported");
break;
}
if (data.params.max_samples != SRC_DEFAULT_MAXSAMPLES) {
LOG(LOGS_WARN, LOGF_Client, "Option maxsamples not supported");
break; break;
} }
@@ -1111,23 +1091,29 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
msg->data.ntp_source.minpoll = htonl(data.params.minpoll); msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll); msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll);
msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll); msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll);
msg->data.ntp_source.min_stratum = htonl(data.params.min_stratum);
msg->data.ntp_source.poll_target = htonl(data.params.poll_target);
msg->data.ntp_source.version = htonl(data.params.version);
msg->data.ntp_source.max_sources = htonl(data.params.max_sources);
msg->data.ntp_source.min_samples = htonl(data.params.min_samples);
msg->data.ntp_source.max_samples = htonl(data.params.max_samples);
msg->data.ntp_source.authkey = htonl(data.params.authkey); msg->data.ntp_source.authkey = htonl(data.params.authkey);
msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay); msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay);
msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio); 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.offset = UTI_FloatHostToNetwork(data.params.offset);
msg->data.ntp_source.flags = htonl( msg->data.ntp_source.flags = htonl(
(data.params.online ? REQ_ADDSRC_ONLINE : 0) | (data.params.online ? REQ_ADDSRC_ONLINE : 0) |
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) | (data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) | (data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 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_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) | (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0)); (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
result = 1; result = 1;
break;
default:
CPS_StatusToString(status, str, sizeof (str));
LOG(LOGS_ERR, LOGF_Client, "%s", str);
break; break;
} }
@@ -1139,7 +1125,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
static int static int
process_cmd_add_server(CMD_Request *msg, char *line) process_cmd_add_server(CMD_Request *msg, char *line)
{ {
msg->command = htons(REQ_ADD_SERVER); msg->command = htons(REQ_ADD_SERVER2);
return process_cmd_add_server_or_peer(msg, line); return process_cmd_add_server_or_peer(msg, line);
} }
@@ -1148,7 +1134,7 @@ process_cmd_add_server(CMD_Request *msg, char *line)
static int static int
process_cmd_add_peer(CMD_Request *msg, char *line) process_cmd_add_peer(CMD_Request *msg, char *line)
{ {
msg->command = htons(REQ_ADD_PEER); msg->command = htons(REQ_ADD_PEER2);
return process_cmd_add_server_or_peer(msg, line); return process_cmd_add_server_or_peer(msg, line);
} }
@@ -1205,6 +1191,7 @@ give_help(void)
"\0\0" "\0\0"
"NTP sources:\0\0" "NTP sources:\0\0"
"activity\0Check how many NTP sources are online/offline\0" "activity\0Check how many NTP sources are online/offline\0"
"ntpdata [<address>]\0Display information about last valid measurement\0"
"add server <address> [options]\0Add new NTP server\0" "add server <address> [options]\0Add new NTP server\0"
"add peer <address> [options]\0Add new NTP peer\0" "add peer <address> [options]\0Add new NTP peer\0"
"delete <address>\0Remove server or peer\0" "delete <address>\0Remove server or peer\0"
@@ -1273,9 +1260,54 @@ give_help(void)
} }
} }
/* ================================================== */
/* Tab-completion when editline/readline is available */
#ifdef FEAT_READLINE
static char *
command_name_generator(const char *text, int state)
{
const char *name, *names[] = {
"accheck", "activity", "add peer", "add server", "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",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
"retries", "rtcdata", "serverstats", "settime", "smoothing", "smoothtime",
"sources", "sources -v", "sourcestats", "sourcestats -v", "timeout",
"tracking", "trimrtc", "waitsync", "writertc",
NULL
};
static int list_index, len;
if (!state) {
list_index = 0;
len = strlen(text);
}
while ((name = names[list_index++])) {
if (strncmp(name, text, len) == 0) {
return strdup(name);
}
}
return NULL;
}
/* ================================================== */
static char **
command_name_completion(const char *text, int start, int end)
{
rl_attempted_completion_over = 1;
return rl_completion_matches(text, command_name_generator);
}
#endif
/* ================================================== */ /* ================================================== */
static unsigned long sequence = 0;
static int max_retries = 2; static int max_retries = 2;
static int initial_timeout = 1000; static int initial_timeout = 1000;
static int proto_version = PROTO_VERSION_NUMBER; static int proto_version = PROTO_VERSION_NUMBER;
@@ -1288,7 +1320,6 @@ static int proto_version = PROTO_VERSION_NUMBER;
static int static int
submit_request(CMD_Request *request, CMD_Reply *reply) submit_request(CMD_Request *request, CMD_Reply *reply)
{ {
unsigned long tx_sequence;
int bad_length, bad_sequence, bad_header; int bad_length, bad_sequence, bad_header;
int select_status; int select_status;
int recv_status; int recv_status;
@@ -1296,52 +1327,72 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
int expected_length; int expected_length;
int command_length; int command_length;
int padding_length; int padding_length;
struct timespec ts_now, ts_start;
struct timeval tv; struct timeval tv;
int timeout; int n_attempts, new_attempt;
int n_attempts; double timeout;
fd_set rdfd, wrfd, exfd; fd_set rdfd, wrfd, exfd;
request->pkt_type = PKT_TYPE_CMD_REQUEST; request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0; request->res1 = 0;
request->res2 = 0; request->res2 = 0;
tx_sequence = sequence++;
request->sequence = htonl(tx_sequence);
request->attempt = 0;
request->pad1 = 0; request->pad1 = 0;
request->pad2 = 0; request->pad2 = 0;
timeout = initial_timeout;
n_attempts = 0; n_attempts = 0;
new_attempt = 1;
do { do {
request->version = proto_version; if (new_attempt) {
command_length = PKL_CommandLength(request); new_attempt = 0;
padding_length = PKL_CommandPaddingLength(request);
assert(command_length > 0 && command_length > padding_length);
/* Zero the padding to avoid sending uninitialized data */ if (n_attempts > max_retries)
memset(((char *)request) + command_length - padding_length, 0, padding_length); return 0;
if (sock_fd < 0) { if (gettimeofday(&tv, NULL))
DEBUG_LOG(LOGF_Client, "No socket to send request"); return 0;
return 0;
UTI_TimevalToTimespec(&tv, &ts_start);
UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
request->attempt = htons(n_attempts);
request->version = proto_version;
command_length = PKL_CommandLength(request);
padding_length = PKL_CommandPaddingLength(request);
assert(command_length > 0 && command_length > padding_length);
n_attempts++;
/* Zero the padding to not send any uninitialized data */
memset(((char *)request) + command_length - padding_length, 0, padding_length);
if (sock_fd < 0) {
DEBUG_LOG(LOGF_Client, "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));
return 0;
}
DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length);
} }
if (send(sock_fd, (void *)request, command_length, 0) < 0) { if (gettimeofday(&tv, NULL))
DEBUG_LOG(LOGF_Client, "Could not send %d bytes : %s",
command_length, strerror(errno));
return 0; return 0;
}
DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length); UTI_TimevalToTimespec(&tv, &ts_now);
/* Increment this for next time */ /* Check if the clock wasn't stepped back */
++ request->attempt; if (UTI_CompareTimespecs(&ts_now, &ts_start) < 0)
ts_start = ts_now;
tv.tv_sec = timeout / 1000; timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
tv.tv_usec = timeout % 1000 * 1000; UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
timeout *= 2; UTI_DoubleToTimeval(timeout, &tv);
DEBUG_LOG(LOGF_Client, "Timeout %f seconds", timeout);
FD_ZERO(&rdfd); FD_ZERO(&rdfd);
FD_ZERO(&wrfd); FD_ZERO(&wrfd);
@@ -1358,14 +1409,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno)); DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno));
} else if (select_status == 0) { } else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */ /* Timeout must have elapsed, try a resend? */
n_attempts ++; new_attempt = 1;
if (n_attempts > max_retries) {
return 0;
}
/* Back to top of loop to do resend */
continue;
} else { } else {
recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_Reply), 0); recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_Reply), 0);
@@ -1373,11 +1417,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
/* If we get connrefused here, it suggests the sendto is /* If we get connrefused here, it suggests the sendto is
going to a dead port */ going to a dead port */
DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno)); DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno));
new_attempt = 1;
n_attempts++;
if (n_attempts > max_retries) {
return 0;
}
} else { } else {
DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status); DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status);
@@ -1392,16 +1432,12 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
expected_length < offsetof(CMD_Reply, data)); expected_length < offsetof(CMD_Reply, data));
if (!bad_length) { if (!bad_length) {
bad_sequence = (ntohl(reply->sequence) != tx_sequence); bad_sequence = reply->sequence != request->sequence;
} else { } else {
bad_sequence = 0; bad_sequence = 0;
} }
if (bad_length || bad_sequence) { if (bad_length || bad_sequence) {
n_attempts++;
if (n_attempts > max_retries) {
return 0;
}
continue; continue;
} }
@@ -1414,10 +1450,6 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
(reply->command != request->command)); (reply->command != request->command));
if (bad_header) { if (bad_header) {
n_attempts++;
if (n_attempts > max_retries) {
return 0;
}
continue; continue;
} }
@@ -1428,6 +1460,8 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
if (proto_version == PROTO_VERSION_NUMBER && if (proto_version == PROTO_VERSION_NUMBER &&
reply->version == PROTO_VERSION_NUMBER - 1) { reply->version == PROTO_VERSION_NUMBER - 1) {
proto_version = PROTO_VERSION_NUMBER - 1; proto_version = PROTO_VERSION_NUMBER - 1;
n_attempts--;
new_attempt = 1;
continue; continue;
} }
#else #else
@@ -1435,9 +1469,8 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#endif #endif
/* Good packet received, print out results */ /* Good packet received, print out results */
DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d seq=%d", DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d",
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status), ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
ntohl(reply->sequence));
break; break;
} }
} }
@@ -1553,7 +1586,7 @@ print_seconds(unsigned long s)
if (s == (uint32_t)-1) { if (s == (uint32_t)-1) {
printf(" -"); printf(" -");
} else if (s <= 1024) { } else if (s < 1200) {
printf("%4ld", s); printf("%4ld", s);
} else if (s < 36000) { } else if (s < 36000) {
printf("%3ldm", s / 60); printf("%3ldm", s / 60);
@@ -1698,7 +1731,7 @@ print_report(const char *format, ...)
unsigned long long_uinteger; unsigned long long_uinteger;
unsigned int uinteger; unsigned int uinteger;
int integer; int integer;
struct timeval *tv; struct timespec *ts;
struct tm *tm; struct tm *tm;
double dbl; double dbl;
@@ -1779,6 +1812,10 @@ print_report(const char *format, ...)
} }
switch (spec) { switch (spec) {
case 'B': /* boolean */
integer = va_arg(ap, int);
printf("%s", integer ? "Yes" : "No");
break;
case 'C': /* clientlog interval */ case 'C': /* clientlog interval */
integer = va_arg(ap, int); integer = va_arg(ap, int);
print_clientlog_interval(integer); print_clientlog_interval(integer);
@@ -1794,6 +1831,63 @@ print_report(const char *format, ...)
long_uinteger = va_arg(ap, unsigned long); long_uinteger = va_arg(ap, unsigned long);
print_seconds(long_uinteger); print_seconds(long_uinteger);
break; break;
case 'L': /* leap status */
integer = va_arg(ap, int);
switch (integer) {
case LEAP_Normal:
string = "Normal";
break;
case LEAP_InsertSecond:
string = "Insert second";
break;
case LEAP_DeleteSecond:
string = "Delete second";
break;
case LEAP_Unsynchronised:
string = "Not synchronised";
break;
default:
string = "Invalid";
break;
}
printf("%s", string);
break;
case 'M': /* NTP mode */
integer = va_arg(ap, int);
switch (integer) {
case MODE_ACTIVE:
string = "Symmetric active";
break;
case MODE_PASSIVE:
string = "Symmetric passive";
break;
case MODE_SERVER:
string = "Server";
break;
default:
string = "Invalid";
break;
}
printf("%s", string);
break;
case 'N': /* Timestamp source */
integer = va_arg(ap, int);
switch (integer) {
case 'D':
string = "Daemon";
break;
case 'K':
string = "Kernel";
break;
case 'H':
string = "Hardware";
break;
default:
string = "Invalid";
break;
}
printf("%s", string);
break;
case 'P': /* frequency in ppm */ case 'P': /* frequency in ppm */
dbl = va_arg(ap, double); dbl = va_arg(ap, double);
if (sign) if (sign)
@@ -1801,10 +1895,9 @@ print_report(const char *format, ...)
else else
print_freq_ppm(dbl); print_freq_ppm(dbl);
break; break;
case 'R': /* reference ID in quad-dotted notation */ case 'R': /* reference ID in hexdecimal */
long_uinteger = va_arg(ap, unsigned long); long_uinteger = va_arg(ap, unsigned long);
printf("%lu.%lu.%lu.%lu", long_uinteger >> 24, (long_uinteger >> 16) & 0xff, printf("%08lX", long_uinteger);
(long_uinteger >> 8) & 0xff, long_uinteger & 0xff);
break; break;
case 'S': /* offset with unit */ case 'S': /* offset with unit */
dbl = va_arg(ap, double); dbl = va_arg(ap, double);
@@ -1813,9 +1906,9 @@ print_report(const char *format, ...)
else else
print_nanoseconds(dbl); print_nanoseconds(dbl);
break; break;
case 'T': /* timeval as date and time in UTC */ case 'T': /* timespec as date and time in UTC */
tv = va_arg(ap, struct timeval *); ts = va_arg(ap, struct timespec *);
tm = gmtime(&tv->tv_sec); tm = gmtime(&ts->tv_sec);
if (!tm) if (!tm)
break; break;
strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm); strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
@@ -1825,9 +1918,14 @@ print_report(const char *format, ...)
long_uinteger = va_arg(ap, unsigned long); long_uinteger = va_arg(ap, unsigned long);
printf("%*lu", width, long_uinteger); printf("%*lu", width, long_uinteger);
break; break;
case 'V': /* timeval as seconds since epoch */ case 'V': /* timespec as seconds since epoch */
tv = va_arg(ap, struct timeval *); ts = va_arg(ap, struct timespec *);
printf("%s", UTI_TimevalToString(tv)); printf("%s", UTI_TimespecToString(ts));
break;
case 'b': /* unsigned int in binary */
uinteger = va_arg(ap, unsigned int);
for (i = prec - 1; i >= 0; i--)
printf("%c", uinteger & 1U << i ? '1' : '0');
break; break;
/* Classic printf specifiers */ /* Classic printf specifiers */
@@ -1898,8 +1996,10 @@ format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id,
snprintf(buf, size, "%s", UTI_IPToString(ip_addr)); snprintf(buf, size, "%s", UTI_IPToString(ip_addr));
} else { } else {
DNS_IPAddress2Name(ip_addr, buf, size); DNS_IPAddress2Name(ip_addr, buf, size);
if (size > trunc_dns) if (trunc_dns > 0 && strlen(buf) > trunc_dns) {
buf[trunc_dns - 1] = '>';
buf[trunc_dns] = '\0'; buf[trunc_dns] = '\0';
}
} }
} }
@@ -2007,7 +2107,7 @@ process_cmd_sources(char *line)
print_report("%c%c %-27s %2d %2d %3o %I %+S[%+S] +/- %S\n", print_report("%c%c %-27s %2d %2d %3o %I %+S[%+S] +/- %S\n",
mode_ch, state_ch, name, mode_ch, state_ch, name,
ntohs(reply.data.source_data.stratum), ntohs(reply.data.source_data.stratum),
ntohs(reply.data.source_data.poll), (int16_t)ntohs(reply.data.source_data.poll),
ntohs(reply.data.source_data.reachability), ntohs(reply.data.source_data.reachability),
(unsigned long)ntohl(reply.data.source_data.since_sample), (unsigned long)ntohl(reply.data.source_data.since_sample),
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas), UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),
@@ -2066,7 +2166,7 @@ process_cmd_sourcestats(char *line)
format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC, format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
ntohl(reply.data.sourcestats.ref_id), &ip_addr); ntohl(reply.data.sourcestats.ref_id), &ip_addr);
print_report("%-25s %3u %3u %I %+P %P %+S %S\n", print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
name, name,
(unsigned long)ntohl(reply.data.sourcestats.n_samples), (unsigned long)ntohl(reply.data.sourcestats.n_samples),
(unsigned long)ntohl(reply.data.sourcestats.n_runs), (unsigned long)ntohl(reply.data.sourcestats.n_runs),
@@ -2091,8 +2191,7 @@ process_cmd_tracking(char *line)
IPAddr ip_addr; IPAddr ip_addr;
uint32_t ref_id; uint32_t ref_id;
char name[50]; char name[50];
struct timeval ref_time; struct timespec ref_time;
const char *leap_status;
request.command = htons(REQ_TRACKING); request.command = htons(REQ_TRACKING);
if (!request_reply(&request, &reply, RPY_TRACKING, 0)) if (!request_reply(&request, &reply, RPY_TRACKING, 0))
@@ -2104,25 +2203,7 @@ process_cmd_tracking(char *line)
format_name(name, sizeof (name), sizeof (name), format_name(name, sizeof (name), sizeof (name),
ip_addr.family == IPADDR_UNSPEC, ref_id, &ip_addr); ip_addr.family == IPADDR_UNSPEC, ref_id, &ip_addr);
switch (ntohs(reply.data.tracking.leap_status)) { UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
case LEAP_Normal:
leap_status = "Normal";
break;
case LEAP_InsertSecond:
leap_status = "Insert second";
break;
case LEAP_DeleteSecond:
leap_status = "Delete second";
break;
case LEAP_Unsynchronised:
leap_status = "Not synchronised";
break;
default:
leap_status = "Unknown";
break;
}
UTI_TimevalNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
print_report("Reference ID : %R (%s)\n" print_report("Reference ID : %R (%s)\n"
"Stratum : %u\n" "Stratum : %u\n"
@@ -2136,7 +2217,7 @@ process_cmd_tracking(char *line)
"Root delay : %.6f seconds\n" "Root delay : %.6f seconds\n"
"Root dispersion : %.6f seconds\n" "Root dispersion : %.6f seconds\n"
"Update interval : %.1f seconds\n" "Update interval : %.1f seconds\n"
"Leap status : %s\n", "Leap status : %L\n",
(unsigned long)ref_id, name, (unsigned long)ref_id, name,
ntohs(reply.data.tracking.stratum), ntohs(reply.data.tracking.stratum),
&ref_time, &ref_time,
@@ -2149,7 +2230,118 @@ process_cmd_tracking(char *line)
UTI_FloatNetworkToHost(reply.data.tracking.root_delay), UTI_FloatNetworkToHost(reply.data.tracking.root_delay),
UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion), UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion),
UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval), UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval),
leap_status, REPORT_END); ntohs(reply.data.tracking.leap_status), REPORT_END);
return 1;
}
/* ================================================== */
static int
process_cmd_ntpdata(char *line)
{
CMD_Request request;
CMD_Reply reply;
IPAddr remote_addr, local_addr;
struct timespec ref_time;
uint32_t i, n_sources;
uint16_t mode;
int specified_addr;
if (*line) {
specified_addr = 1;
n_sources = 1;
} else {
specified_addr = 0;
request.command = htons(REQ_N_SOURCES);
if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
return 0;
n_sources = ntohl(reply.data.n_sources.n_sources);
}
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");
return 0;
}
} else {
request.command = htons(REQ_SOURCE_DATA);
request.data.source_data.index = htonl(i);
if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
return 0;
mode = ntohs(reply.data.source_data.mode);
if (mode != RPY_SD_MD_CLIENT && mode != RPY_SD_MD_PEER)
continue;
UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr);
}
request.command = htons(REQ_NTP_DATA);
UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
return 0;
UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
UTI_IPNetworkToHost(&reply.data.ntp_data.local_addr, &local_addr);
UTI_TimespecNetworkToHost(&reply.data.ntp_data.ref_time, &ref_time);
if (!specified_addr && !csv_mode)
printf("\n");
print_report("Remote address : %s (%R)\n"
"Remote port : %u\n"
"Local address : %s (%R)\n"
"Leap status : %L\n"
"Version : %u\n"
"Mode : %M\n"
"Stratum : %u\n"
"Poll interval : %d (%.0f seconds)\n"
"Precision : %d (%.9f seconds)\n"
"Root delay : %.6f seconds\n"
"Root dispersion : %.6f seconds\n"
"Reference ID : %R\n"
"Reference time : %T\n"
"Offset : %+.9f seconds\n"
"Peer delay : %.9f seconds\n"
"Peer dispersion : %.9f seconds\n"
"Response time : %.9f seconds\n"
"Jitter asymmetry: %+.2f\n"
"NTP tests : %.3b %.3b %.4b\n"
"Interleaved : %B\n"
"Authenticated : %B\n"
"TX timestamping : %N\n"
"RX timestamping : %N\n"
"Total TX : %U\n"
"Total RX : %U\n"
"Total valid RX : %U\n",
UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port),
UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
reply.data.ntp_data.leap, reply.data.ntp_data.version,
reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
(unsigned long)ntohl(reply.data.ntp_data.ref_id), &ref_time,
UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion),
UTI_FloatNetworkToHost(reply.data.ntp_data.response_time),
UTI_FloatNetworkToHost(reply.data.ntp_data.jitter_asymmetry),
ntohs(reply.data.ntp_data.flags) >> 7,
ntohs(reply.data.ntp_data.flags) >> 4,
ntohs(reply.data.ntp_data.flags),
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
(unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
(unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
(unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
REPORT_END);
}
return 1; return 1;
} }
@@ -2196,13 +2388,13 @@ process_cmd_smoothing(char *line)
flags = ntohl(reply.data.smoothing.flags); flags = ntohl(reply.data.smoothing.flags);
print_report("Active : %s %s\n" print_report("Active : %B %s\n"
"Offset : %+.9f seconds\n" "Offset : %+.9f seconds\n"
"Frequency : %+.6f ppm\n" "Frequency : %+.6f ppm\n"
"Wander : %+.6f ppm per second\n" "Wander : %+.6f ppm per second\n"
"Last update : %.1f seconds ago\n" "Last update : %.1f seconds ago\n"
"Remaining time : %.1f seconds\n", "Remaining time : %.1f seconds\n",
flags & RPY_SMT_FLAG_ACTIVE ? "Yes" : "No", !!(flags & RPY_SMT_FLAG_ACTIVE),
flags & RPY_SMT_FLAG_LEAPONLY ? "(leap second only)" : "", flags & RPY_SMT_FLAG_LEAPONLY ? "(leap second only)" : "",
UTI_FloatNetworkToHost(reply.data.smoothing.offset), UTI_FloatNetworkToHost(reply.data.smoothing.offset),
UTI_FloatNetworkToHost(reply.data.smoothing.freq_ppm), UTI_FloatNetworkToHost(reply.data.smoothing.freq_ppm),
@@ -2240,13 +2432,13 @@ process_cmd_rtcreport(char *line)
{ {
CMD_Request request; CMD_Request request;
CMD_Reply reply; CMD_Reply reply;
struct timeval ref_time; struct timespec ref_time;
request.command = htons(REQ_RTCREPORT); request.command = htons(REQ_RTCREPORT);
if (!request_reply(&request, &reply, RPY_RTC, 0)) if (!request_reply(&request, &reply, RPY_RTC, 0))
return 0; return 0;
UTI_TimevalNetworkToHost(&reply.data.rtc.ref_time, &ref_time); UTI_TimespecNetworkToHost(&reply.data.rtc.ref_time, &ref_time);
print_report("RTC ref time (UTC) : %T\n" print_report("RTC ref time (UTC) : %T\n"
"Number of samples : %u\n" "Number of samples : %u\n"
@@ -2302,7 +2494,7 @@ process_cmd_clients(char *line)
if (ip.family == IPADDR_UNSPEC) if (ip.family == IPADDR_UNSPEC)
continue; continue;
format_name(name, sizeof (name), sizeof (name), 0, 0, &ip); format_name(name, sizeof (name), 25, 0, 0, &ip);
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n", print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
name, name,
@@ -2338,7 +2530,7 @@ process_cmd_manual_list(const char *line)
CMD_Reply reply; CMD_Reply reply;
uint32_t i, n_samples; uint32_t i, n_samples;
RPY_ManualListSample *sample; RPY_ManualListSample *sample;
struct timeval when; struct timespec when;
request.command = htons(REQ_MANUAL_LIST); request.command = htons(REQ_MANUAL_LIST);
if (!request_reply(&request, &reply, RPY_MANUAL_LIST, 0)) if (!request_reply(&request, &reply, RPY_MANUAL_LIST, 0))
@@ -2351,7 +2543,7 @@ process_cmd_manual_list(const char *line)
for (i = 0; i < n_samples; i++) { for (i = 0; i < n_samples; i++) {
sample = &reply.data.manual_list.samples[i]; sample = &reply.data.manual_list.samples[i];
UTI_TimevalNetworkToHost(&sample->when, &when); UTI_TimespecNetworkToHost(&sample->when, &when);
print_report("%2d %s %10.2f %10.2f %10.2f\n", print_report("%2d %s %10.2f %10.2f %10.2f\n",
i, UTI_TimeToLogForm(when.tv_sec), i, UTI_TimeToLogForm(when.tv_sec),
@@ -2386,7 +2578,7 @@ process_cmd_manual_delete(CMD_Request *msg, const char *line)
static int static int
process_cmd_settime(char *line) process_cmd_settime(char *line)
{ {
struct timeval ts; struct timespec ts;
time_t now, new_time; time_t now, new_time;
CMD_Request request; CMD_Request request;
CMD_Reply reply; CMD_Reply reply;
@@ -2401,8 +2593,8 @@ process_cmd_settime(char *line)
printf("510 - Could not parse date string\n"); printf("510 - Could not parse date string\n");
} else { } else {
ts.tv_sec = new_time; ts.tv_sec = new_time;
ts.tv_usec = 0; ts.tv_nsec = 0;
UTI_TimevalHostToNetwork(&ts, &request.data.settime.ts); UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts);
request.command = htons(REQ_SETTIME); request.command = htons(REQ_SETTIME);
if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP, 1)) { if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP, 1)) {
offset_cs = ntohl(reply.data.manual_timestamp.centiseconds); offset_cs = ntohl(reply.data.manual_timestamp.centiseconds);
@@ -2517,6 +2709,7 @@ process_cmd_waitsync(char *line)
{ {
CMD_Request request; CMD_Request request;
CMD_Reply reply; CMD_Reply reply;
IPAddr ip_addr;
uint32_t ref_id; uint32_t ref_id;
double correction, skew_ppm, max_correction, max_skew_ppm, interval; double correction, skew_ppm, max_correction, max_skew_ppm, interval;
int ret = 0, max_tries, i; int ret = 0, max_tries, i;
@@ -2538,6 +2731,7 @@ process_cmd_waitsync(char *line)
for (i = 1; ; i++) { for (i = 1; ; i++) {
if (request_reply(&request, &reply, RPY_TRACKING, 0)) { if (request_reply(&request, &reply, RPY_TRACKING, 0)) {
ref_id = ntohl(reply.data.tracking.ref_id); ref_id = ntohl(reply.data.tracking.ref_id);
UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction); correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction);
correction = fabs(correction); correction = fabs(correction);
@@ -2546,7 +2740,8 @@ process_cmd_waitsync(char *line)
print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n", print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END); i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END);
if (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */ && if ((ip_addr.family != IPADDR_UNSPEC ||
(ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
(max_correction == 0.0 || correction <= max_correction) && (max_correction == 0.0 || correction <= max_correction) &&
(max_skew_ppm == 0.0 || skew_ppm <= max_skew_ppm)) { (max_skew_ppm == 0.0 || skew_ppm <= max_skew_ppm)) {
ret = 1; ret = 1;
@@ -2769,6 +2964,9 @@ process_line(char *line)
do_normal_submit = process_cmd_minpoll(&tx_message, line); do_normal_submit = process_cmd_minpoll(&tx_message, line);
} else if (!strcmp(command, "minstratum")) { } else if (!strcmp(command, "minstratum")) {
do_normal_submit = process_cmd_minstratum(&tx_message, line); do_normal_submit = process_cmd_minstratum(&tx_message, line);
} else if (!strcmp(command, "ntpdata")) {
do_normal_submit = 0;
ret = process_cmd_ntpdata(line);
} else if (!strcmp(command, "offline")) { } else if (!strcmp(command, "offline")) {
do_normal_submit = process_cmd_offline(&tx_message, line); do_normal_submit = process_cmd_offline(&tx_message, line);
} else if (!strcmp(command, "online")) { } else if (!strcmp(command, "online")) {

View File

@@ -39,6 +39,7 @@
#include "clientlog.h" #include "clientlog.h"
#include "conf.h" #include "conf.h"
#include "memory.h" #include "memory.h"
#include "ntp.h"
#include "reports.h" #include "reports.h"
#include "util.h" #include "util.h"
#include "logging.h" #include "logging.h"
@@ -57,6 +58,8 @@ typedef struct {
int8_t cmd_rate; int8_t cmd_rate;
int8_t ntp_timeout_rate; int8_t ntp_timeout_rate;
uint8_t flags; uint8_t flags;
NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts;
} Record; } Record;
/* Hash table of records, there is a fixed number of records per slot */ /* Hash table of records, there is a fixed number of records per slot */
@@ -83,6 +86,10 @@ static unsigned int max_slots;
#define TS_FRAC 4 #define TS_FRAC 4
#define INVALID_TS 0 #define INVALID_TS 0
/* Static offset included in conversion to the fixed-point timestamps to
randomise their alignment */
static uint32_t ts_offset;
/* Request rates are saved in the record as 8-bit scaled log2 values */ /* Request rates are saved in the record as 8-bit scaled log2 values */
#define RATE_SCALE 4 #define RATE_SCALE 4
#define MIN_RATE (-14 * RATE_SCALE) #define MIN_RATE (-14 * RATE_SCALE)
@@ -92,7 +99,7 @@ static unsigned int max_slots;
number of tokens spent on response are determined from configured number of tokens spent on response are determined from configured
minimum inverval between responses (in log2) and burst length. */ minimum inverval between responses (in log2) and burst length. */
#define MIN_LIMIT_INTERVAL (-TS_FRAC) #define MIN_LIMIT_INTERVAL (-15 - TS_FRAC)
#define MAX_LIMIT_INTERVAL 12 #define MAX_LIMIT_INTERVAL 12
#define MIN_LIMIT_BURST 1 #define MIN_LIMIT_BURST 1
#define MAX_LIMIT_BURST 255 #define MAX_LIMIT_BURST 255
@@ -102,7 +109,8 @@ static uint16_t max_cmd_tokens;
static uint16_t ntp_tokens_per_packet; static uint16_t ntp_tokens_per_packet;
static uint16_t cmd_tokens_per_packet; static uint16_t cmd_tokens_per_packet;
/* Reduction of token rates to avoid overflow of 16-bit counters */ /* Reduction of token rates to avoid overflow of 16-bit counters. Negative
shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
static int ntp_token_shift; static int ntp_token_shift;
static int cmd_token_shift; static int cmd_token_shift;
@@ -111,7 +119,7 @@ static int cmd_token_shift;
prevent an attacker sending requests with spoofed source address prevent an attacker sending requests with spoofed source address
from blocking responses to the address completely. */ from blocking responses to the address completely. */
#define MIN_LEAK_RATE 1 #define MIN_LEAK_RATE 0
#define MAX_LEAK_RATE 4 #define MAX_LEAK_RATE 4
static int ntp_leak_rate; static int ntp_leak_rate;
@@ -130,6 +138,8 @@ static uint32_t total_ntp_drops;
static uint32_t total_cmd_drops; static uint32_t total_cmd_drops;
static uint32_t total_record_drops; static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U
/* ================================================== */ /* ================================================== */
static int expand_hashtable(void); static int expand_hashtable(void);
@@ -155,7 +165,7 @@ get_record(IPAddr *ip)
time_t last_hit, oldest_hit = 0; time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record; Record *record, *oldest_record;
if (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6) if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
return NULL; return NULL;
while (1) { while (1) {
@@ -206,6 +216,8 @@ get_record(IPAddr *ip)
record->ntp_rate = record->cmd_rate = INVALID_RATE; record->ntp_rate = record->cmd_rate = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE; record->ntp_timeout_rate = INVALID_RATE;
record->flags = 0; record->flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts);
return record; return record;
} }
@@ -266,10 +278,17 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL); interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST); burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST);
/* Find smallest shift with which the maximum number fits in 16 bits */ if (interval >= -TS_FRAC) {
for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) { /* Find the smallest shift with which the maximum number fits in 16 bits */
if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16) for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
break; if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
break;
}
} else {
/* Coarse rate limiting */
*token_shift = interval + TS_FRAC;
*tokens_per_packet = 1;
burst = MAX(1U << -*token_shift, burst);
} }
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift); *tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
@@ -286,29 +305,19 @@ CLG_Initialise(void)
{ {
int interval, burst, leak_rate; int interval, burst, leak_rate;
max_ntp_tokens = max_cmd_tokens = 0; CNF_GetNTPRateLimit(&interval, &burst, &leak_rate);
ntp_tokens_per_packet = cmd_tokens_per_packet = 0; set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
ntp_token_shift = cmd_token_shift = 0; &ntp_token_shift);
ntp_leak_rate = cmd_leak_rate = 0; ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) { CNF_GetCommandRateLimit(&interval, &burst, &leak_rate);
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet, set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet,
&ntp_token_shift); &cmd_token_shift);
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE); cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
}
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) {
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet,
&cmd_token_shift);
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
}
active = !CNF_GetNoClientLog(); active = !CNF_GetNoClientLog();
if (!active) { if (!active)
if (ntp_leak_rate || cmd_leak_rate)
LOG_FATAL(LOGF_ClientLog, "ratelimit cannot be used with noclientlog");
return; return;
}
/* Calculate the maximum number of slots that can be allocated in the /* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash configured memory limit. Take into account expanding of the hash
@@ -320,6 +329,9 @@ CLG_Initialise(void)
records = NULL; records = NULL;
expand_hashtable(); expand_hashtable();
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
} }
/* ================================================== */ /* ================================================== */
@@ -336,23 +348,30 @@ CLG_Finalise(void)
/* ================================================== */ /* ================================================== */
static uint32_t static uint32_t
get_ts_from_timeval(struct timeval *tv) get_ts_from_timespec(struct timespec *ts)
{ {
uint32_t sec = tv->tv_sec, usec = tv->tv_usec; uint32_t sec = ts->tv_sec, nsec = ts->tv_nsec;
return sec << TS_FRAC | (4295U * usec - (usec >> 5)) >> (32 - TS_FRAC); nsec += ts_offset;
if (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
sec++;
}
/* This is fast and accurate enough */
return sec << TS_FRAC | (140740U * (nsec >> 15)) >> (32 - TS_FRAC);
} }
/* ================================================== */ /* ================================================== */
static void static void
update_record(struct timeval *now, uint32_t *last_hit, uint32_t *hits, update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate) uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
{ {
uint32_t interval, now_ts, prev_hit, new_tokens; uint32_t interval, now_ts, prev_hit, new_tokens;
int interval2; int interval2;
now_ts = get_ts_from_timeval(now); now_ts = get_ts_from_timespec(now);
prev_hit = *last_hit; prev_hit = *last_hit;
*last_hit = now_ts; *last_hit = now_ts;
@@ -363,7 +382,12 @@ update_record(struct timeval *now, uint32_t *last_hit, uint32_t *hits,
if (prev_hit == INVALID_TS || (int32_t)interval < 0) if (prev_hit == INVALID_TS || (int32_t)interval < 0)
return; return;
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift); if (token_shift >= 0)
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift);
else if (now_ts - prev_hit > max_tokens)
new_tokens = max_tokens;
else
new_tokens = (now_ts - prev_hit) << -token_shift;
*tokens = MIN(*tokens + new_tokens, max_tokens); *tokens = MIN(*tokens + new_tokens, max_tokens);
/* Convert the interval to scaled and rounded log2 */ /* Convert the interval to scaled and rounded log2 */
@@ -405,15 +429,26 @@ get_index(Record *record)
/* ================================================== */ /* ================================================== */
int int
CLG_LogNTPAccess(IPAddr *client, struct timeval *now) CLG_GetClientIndex(IPAddr *client)
{
Record *record;
record = get_record(client);
if (record == NULL)
return -1;
return get_index(record);
}
/* ================================================== */
int
CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
{ {
Record *record; Record *record;
total_ntp_hits++; total_ntp_hits++;
if (!active)
return -1;
record = get_record(client); record = get_record(client);
if (record == NULL) if (record == NULL)
return -1; return -1;
@@ -435,15 +470,12 @@ CLG_LogNTPAccess(IPAddr *client, struct timeval *now)
/* ================================================== */ /* ================================================== */
int int
CLG_LogCommandAccess(IPAddr *client, struct timeval *now) CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
{ {
Record *record; Record *record;
total_cmd_hits++; total_cmd_hits++;
if (!active)
return -1;
record = get_record(client); record = get_record(client);
if (record == NULL) if (record == NULL)
return -1; return -1;
@@ -488,7 +520,7 @@ CLG_LimitNTPResponseRate(int index)
Record *record; Record *record;
int drop; int drop;
if (!ntp_tokens_per_packet) if (!ntp_leak_rate)
return 0; return 0;
record = ARR_GetElement(records, index); record = ARR_GetElement(records, index);
@@ -529,7 +561,7 @@ CLG_LimitCommandResponseRate(int index)
{ {
Record *record; Record *record;
if (!cmd_tokens_per_packet) if (!cmd_leak_rate)
return 0; return 0;
record = ARR_GetElement(records, index); record = ARR_GetElement(records, index);
@@ -552,7 +584,19 @@ CLG_LimitCommandResponseRate(int index)
/* ================================================== */ /* ================================================== */
extern int void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
{
Record *record;
record = ARR_GetElement(records, index);
*rx_ts = &record->ntp_rx_ts;
*tx_ts = &record->ntp_tx_ts;
}
/* ================================================== */
int
CLG_GetNumberOfIndices(void) CLG_GetNumberOfIndices(void)
{ {
if (!active) if (!active)
@@ -586,7 +630,7 @@ static uint32_t get_last_ago(uint32_t x, uint32_t y)
/* ================================================== */ /* ================================================== */
int int
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now) CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now)
{ {
Record *record; Record *record;
uint32_t now_ts; uint32_t now_ts;
@@ -599,7 +643,7 @@ CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *repo
if (record->ip_addr.family == IPADDR_UNSPEC) if (record->ip_addr.family == IPADDR_UNSPEC)
return 0; return 0;
now_ts = get_ts_from_timeval(now); now_ts = get_ts_from_timespec(now);
report->ip_addr = record->ip_addr; report->ip_addr = record->ip_addr;
report->ntp_hits = record->ntp_hits; report->ntp_hits = record->ntp_hits;

View File

@@ -33,15 +33,17 @@
extern void CLG_Initialise(void); extern void CLG_Initialise(void);
extern void CLG_Finalise(void); extern void CLG_Finalise(void);
extern int CLG_LogNTPAccess(IPAddr *client, struct timeval *now); extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogCommandAccess(IPAddr *client, struct timeval *now); extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index); extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index); extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
/* And some reporting functions, for use by chronyc. */ /* And some reporting functions, for use by chronyc. */
extern int CLG_GetNumberOfIndices(void); extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now); extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report); extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */ #endif /* GOT_CLIENTLOG_H */

117
cmdmon.c
View File

@@ -133,6 +133,9 @@ static const char permissions[] = {
PERMIT_AUTH, /* SERVER_STATS */ PERMIT_AUTH, /* SERVER_STATS */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */ PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
PERMIT_AUTH, /* LOCAL2 */ PERMIT_AUTH, /* LOCAL2 */
PERMIT_AUTH, /* NTP_DATA */
PERMIT_AUTH, /* ADD_SERVER2 */
PERMIT_AUTH, /* ADD_PEER2 */
}; };
/* ================================================== */ /* ================================================== */
@@ -143,7 +146,7 @@ static ADF_AuthTable access_auth_table;
/* ================================================== */ /* ================================================== */
/* Forward prototypes */ /* Forward prototypes */
static void read_from_cmd_socket(void *anything); static void read_from_cmd_socket(int sock_fd, int event, void *anything);
/* ================================================== */ /* ================================================== */
@@ -242,7 +245,7 @@ prepare_socket(int family, int port_number)
} }
/* Register handler for read events on the socket */ /* Register handler for read events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_cmd_socket, (void *)(long)sock_fd); SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_from_cmd_socket, NULL);
return sock_fd; return sock_fd;
} }
@@ -325,19 +328,19 @@ void
CAM_Finalise(void) CAM_Finalise(void)
{ {
if (sock_fdu >= 0) { if (sock_fdu >= 0) {
SCH_RemoveInputFileHandler(sock_fdu); SCH_RemoveFileHandler(sock_fdu);
close(sock_fdu); close(sock_fdu);
unlink(CNF_GetBindCommandPath()); unlink(CNF_GetBindCommandPath());
} }
sock_fdu = -1; sock_fdu = -1;
if (sock_fd4 >= 0) { if (sock_fd4 >= 0) {
SCH_RemoveInputFileHandler(sock_fd4); SCH_RemoveFileHandler(sock_fd4);
close(sock_fd4); close(sock_fd4);
} }
sock_fd4 = -1; sock_fd4 = -1;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
if (sock_fd6 >= 0) { if (sock_fd6 >= 0) {
SCH_RemoveInputFileHandler(sock_fd6); SCH_RemoveFileHandler(sock_fd6);
close(sock_fd6); close(sock_fd6);
} }
sock_fd6 = -1; sock_fd6 = -1;
@@ -564,10 +567,10 @@ handle_modify_makestep(CMD_Request *rx_message, CMD_Reply *tx_message)
static void static void
handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message) handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
struct timeval ts; struct timespec ts;
long offset_cs; long offset_cs;
double dfreq_ppm, new_afreq_ppm; double dfreq_ppm, new_afreq_ppm;
UTI_TimevalNetworkToHost(&rx_message->data.settime.ts, &ts); UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts);
if (!MNL_IsEnabled()) { if (!MNL_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED); tx_message->status = htons(STT_NOTENABLED);
} else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) { } else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
@@ -634,7 +637,7 @@ static void
handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message) handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
RPT_SourceReport report; RPT_SourceReport report;
struct timeval now_corr; struct timespec now_corr;
/* Get data */ /* Get data */
SCH_GetLastEventTime(&now_corr, NULL, NULL); SCH_GetLastEventTime(&now_corr, NULL, NULL);
@@ -777,26 +780,29 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll); params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll); params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll); params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
params.min_stratum = ntohl(rx_message->data.ntp_source.min_stratum);
params.poll_target = ntohl(rx_message->data.ntp_source.poll_target);
params.version = ntohl(rx_message->data.ntp_source.version);
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.authkey = ntohl(rx_message->data.ntp_source.authkey); 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.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0; params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0; 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.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.sel_options = 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_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) | (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) | (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0); (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
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);
/* not transmitted in cmdmon protocol yet */
params.min_stratum = SRC_DEFAULT_MINSTRATUM;
params.poll_target = SRC_DEFAULT_POLLTARGET;
params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
params.version = NTP_VERSION;
params.max_sources = SRC_DEFAULT_MAXSOURCES;
params.min_samples = SRC_DEFAULT_MINSAMPLES;
params.max_samples = SRC_DEFAULT_MAXSAMPLES;
status = NSR_AddSource(&rem_addr, type, &params); status = NSR_AddSource(&rem_addr, type, &params);
switch (status) { switch (status) {
@@ -898,7 +904,7 @@ handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPHostToNetwork(&rpt.ip_addr, &tx_message->data.tracking.ip_addr); UTI_IPHostToNetwork(&rpt.ip_addr, &tx_message->data.tracking.ip_addr);
tx_message->data.tracking.stratum = htons(rpt.stratum); tx_message->data.tracking.stratum = htons(rpt.stratum);
tx_message->data.tracking.leap_status = htons(rpt.leap_status); tx_message->data.tracking.leap_status = htons(rpt.leap_status);
UTI_TimevalHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time); UTI_TimespecHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time);
tx_message->data.tracking.current_correction = UTI_FloatHostToNetwork(rpt.current_correction); tx_message->data.tracking.current_correction = UTI_FloatHostToNetwork(rpt.current_correction);
tx_message->data.tracking.last_offset = UTI_FloatHostToNetwork(rpt.last_offset); tx_message->data.tracking.last_offset = UTI_FloatHostToNetwork(rpt.last_offset);
tx_message->data.tracking.rms_offset = UTI_FloatHostToNetwork(rpt.rms_offset); tx_message->data.tracking.rms_offset = UTI_FloatHostToNetwork(rpt.rms_offset);
@@ -916,7 +922,7 @@ static void
handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message) handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
RPT_SmoothingReport report; RPT_SmoothingReport report;
struct timeval now; struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL); SCH_GetLastEventTime(&now, NULL, NULL);
@@ -940,7 +946,7 @@ handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message)
static void static void
handle_smoothtime(CMD_Request *rx_message, CMD_Reply *tx_message) handle_smoothtime(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
struct timeval now; struct timespec now;
int option; int option;
if (!SMT_IsEnabled()) { if (!SMT_IsEnabled()) {
@@ -971,7 +977,7 @@ handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
int status; int status;
RPT_SourcestatsReport report; RPT_SourcestatsReport report;
struct timeval now_corr; struct timespec now_corr;
SCH_GetLastEventTime(&now_corr, NULL, NULL); SCH_GetLastEventTime(&now_corr, NULL, NULL);
status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index), status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index),
@@ -1004,7 +1010,7 @@ handle_rtcreport(CMD_Request *rx_message, CMD_Reply *tx_message)
status = RTC_GetReport(&report); status = RTC_GetReport(&report);
if (status) { if (status) {
tx_message->reply = htons(RPY_RTC); tx_message->reply = htons(RPY_RTC);
UTI_TimevalHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time); UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time);
tx_message->data.rtc.n_samples = htons(report.n_samples); tx_message->data.rtc.n_samples = htons(report.n_samples);
tx_message->data.rtc.n_runs = htons(report.n_runs); tx_message->data.rtc.n_runs = htons(report.n_runs);
tx_message->data.rtc.span_seconds = htonl(report.span_seconds); tx_message->data.rtc.span_seconds = htonl(report.span_seconds);
@@ -1041,7 +1047,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
RPY_ClientAccesses_Client *client; RPY_ClientAccesses_Client *client;
int n_indices; int n_indices;
uint32_t i, j, req_first_index, req_n_clients; uint32_t i, j, req_first_index, req_n_clients;
struct timeval now; struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL); SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1100,7 +1106,7 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.manual_list.n_samples = htonl(n_samples); tx_message->data.manual_list.n_samples = htonl(n_samples);
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i]; sample = &tx_message->data.manual_list.samples[i];
UTI_TimevalHostToNetwork(&report[i].when, &sample->when); UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
sample->slewed_offset = UTI_FloatHostToNetwork(report[i].slewed_offset); sample->slewed_offset = UTI_FloatHostToNetwork(report[i].slewed_offset);
sample->orig_offset = UTI_FloatHostToNetwork(report[i].orig_offset); sample->orig_offset = UTI_FloatHostToNetwork(report[i].orig_offset);
sample->residual = UTI_FloatHostToNetwork(report[i].residual); sample->residual = UTI_FloatHostToNetwork(report[i].residual);
@@ -1185,26 +1191,69 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.server_stats.log_drops = htonl(report.log_drops); tx_message->data.server_stats.log_drops = htonl(report.log_drops);
} }
/* ================================================== */
static void
handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_NTPReport report;
UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &report.remote_addr);
if (!NSR_GetNTPReport(&report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_NTP_DATA);
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
tx_message->data.ntp_data.leap = report.leap;
tx_message->data.ntp_data.version = report.version;
tx_message->data.ntp_data.mode = report.mode;
tx_message->data.ntp_data.stratum = report.stratum;
tx_message->data.ntp_data.poll = report.poll;
tx_message->data.ntp_data.precision = report.precision;
tx_message->data.ntp_data.root_delay = UTI_FloatHostToNetwork(report.root_delay);
tx_message->data.ntp_data.root_dispersion = UTI_FloatHostToNetwork(report.root_dispersion);
tx_message->data.ntp_data.ref_id = htonl(report.ref_id);
UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.ntp_data.ref_time);
tx_message->data.ntp_data.offset = UTI_FloatHostToNetwork(report.offset);
tx_message->data.ntp_data.peer_delay = UTI_FloatHostToNetwork(report.peer_delay);
tx_message->data.ntp_data.peer_dispersion = UTI_FloatHostToNetwork(report.peer_dispersion);
tx_message->data.ntp_data.response_time = UTI_FloatHostToNetwork(report.response_time);
tx_message->data.ntp_data.jitter_asymmetry = UTI_FloatHostToNetwork(report.jitter_asymmetry);
tx_message->data.ntp_data.flags = htons((report.tests & RPY_NTP_FLAGS_TESTS) |
(report.interleaved ? RPY_NTP_FLAG_INTERLEAVED : 0) |
(report.authenticated ? RPY_NTP_FLAG_AUTHENTICATED : 0));
tx_message->data.ntp_data.tx_tss_char = report.tx_tss_char;
tx_message->data.ntp_data.rx_tss_char = report.rx_tss_char;
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
/* ================================================== */ /* ================================================== */
/* Read a packet and process it */ /* Read a packet and process it */
static void static void
read_from_cmd_socket(void *anything) read_from_cmd_socket(int sock_fd, int event, void *anything)
{ {
CMD_Request rx_message; CMD_Request rx_message;
CMD_Reply tx_message; CMD_Reply tx_message;
int status, read_length, expected_length, rx_message_length; int status, read_length, expected_length, rx_message_length;
int localhost, allowed, sock_fd, log_index; int localhost, allowed, log_index;
union sockaddr_all where_from; union sockaddr_all where_from;
socklen_t from_length; socklen_t from_length;
IPAddr remote_ip; IPAddr remote_ip;
unsigned short remote_port, rx_command; unsigned short remote_port, rx_command;
struct timeval now, cooked_now; struct timespec now, cooked_now;
rx_message_length = sizeof(rx_message); rx_message_length = sizeof(rx_message);
from_length = sizeof(where_from); from_length = sizeof(where_from);
sock_fd = (long)anything;
status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0, status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0,
&where_from.sa, &from_length); &where_from.sa, &from_length);
@@ -1477,11 +1526,11 @@ read_from_cmd_socket(void *anything)
handle_cmdaccheck(&rx_message, &tx_message); handle_cmdaccheck(&rx_message, &tx_message);
break; break;
case REQ_ADD_SERVER: case REQ_ADD_SERVER2:
handle_add_source(NTP_SERVER, &rx_message, &tx_message); handle_add_source(NTP_SERVER, &rx_message, &tx_message);
break; break;
case REQ_ADD_PEER: case REQ_ADD_PEER2:
handle_add_source(NTP_PEER, &rx_message, &tx_message); handle_add_source(NTP_PEER, &rx_message, &tx_message);
break; break;
@@ -1573,6 +1622,10 @@ read_from_cmd_socket(void *anything)
handle_server_stats(&rx_message, &tx_message); handle_server_stats(&rx_message, &tx_message);
break; break;
case REQ_NTP_DATA:
handle_ntp_data(&rx_message, &tx_message);
break;
default: default:
DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command); DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED); tx_message.status = htons(STT_FAILED);

View File

@@ -39,186 +39,115 @@
/* ================================================== */ /* ================================================== */
CPS_Status int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{ {
char *hostname, *cmd; char *hostname, *cmd;
int n, done; int n;
CPS_Status result;
src->port = SRC_DEFAULT_PORT; src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL; src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL; src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
src->params.online = 1;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL; src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = 0;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.authkey = INACTIVE_AUTHKEY; src->params.authkey = INACTIVE_AUTHKEY;
src->params.max_delay = SRC_DEFAULT_MAXDELAY; src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.online = 1; src->params.offset = 0.0;
src->params.auto_offline = 0;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = NTP_VERSION;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.sel_options = 0;
result = CPS_Success;
hostname = line; hostname = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (!*hostname) { if (!*hostname)
result = CPS_BadHost; return 0;
} else {
src->name = hostname;
/* Parse subfields */ src->name = hostname;
done = 0;
do {
cmd = line;
line = CPS_SplitWord(line);
if (*cmd) { /* Parse options */
if (!strcasecmp(cmd, "port")) { for (; *line; line += n) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1) { cmd = line;
result = CPS_BadPort; line = CPS_SplitWord(line);
done = 1; n = 0;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) {
result = CPS_BadMinpoll;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) {
result = CPS_BadMaxpoll;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) {
result = CPS_BadPresend;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) {
result = CPS_BadMaxdelaydevratio;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) {
result = CPS_BadMaxdelayratio;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) {
result = CPS_BadMaxdelay;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) {
result = CPS_BadKey;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0;
} else if (!strcasecmp(cmd, "auto_offline")) { if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1; src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "iburst")) {
} else if (!strcasecmp(cmd, "iburst")) { src->params.iburst = 1;
src->params.iburst = 1; } else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0;
} else if (!strcasecmp(cmd, "minstratum")) { } else if (!strcasecmp(cmd, "noselect")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) { src->params.sel_options |= SRC_SELECT_NOSELECT;
result = CPS_BadMinstratum; } else if (!strcasecmp(cmd, "prefer")) {
done = 1; src->params.sel_options |= SRC_SELECT_PREFER;
} else { } else if (!strcasecmp(cmd, "require")) {
line += n; src->params.sel_options |= SRC_SELECT_REQUIRE;
} } else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "polltarget")) { } else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) { if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
result = CPS_BadPolltarget; src->params.authkey == INACTIVE_AUTHKEY)
done = 1; return 0;
} else { } else if (!strcasecmp(cmd, "maxdelay")) {
line += n; if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
} return 0;
} else if (!strcasecmp(cmd, "maxdelayratio")) {
} else if (!strcasecmp(cmd, "noselect")) { if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
src->params.sel_options |= SRC_SELECT_NOSELECT; return 0;
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
} else if (!strcasecmp(cmd, "prefer")) { if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
src->params.sel_options |= SRC_SELECT_PREFER; return 0;
} else if (!strcasecmp(cmd, "maxpoll")) {
} else if (!strcasecmp(cmd, "trust")) { if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
src->params.sel_options |= SRC_SELECT_TRUST; return 0;
} else if (!strcasecmp(cmd, "maxsamples")) {
} else if (!strcasecmp(cmd, "require")) { if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
src->params.sel_options |= SRC_SELECT_REQUIRE; return 0;
} else if (!strcasecmp(cmd, "maxsources")) {
} else if (!strcasecmp(cmd, "version")) { if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
if (sscanf(line, "%d%n", &src->params.version, &n) != 1) { return 0;
result = CPS_BadVersion; } else if (!strcasecmp(cmd, "minpoll")) {
done = 1; if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
} else { return 0;
line += n; } else if (!strcasecmp(cmd, "minsamples")) {
} if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxsources")) { } else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) { if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
result = CPS_BadMaxsources; return 0;
done = 1; } else if (!strcasecmp(cmd, "offset")) {
} else { if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
line += n; return 0;
} } else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1)
} else if (!strcasecmp(cmd, "minsamples")) { return 0;
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1) { } else if (!strcasecmp(cmd, "polltarget")) {
result = CPS_BadMinsamples; if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
done = 1; return 0;
} else { } else if (!strcasecmp(cmd, "presend")) {
line += n; if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
} return 0;
} else if (!strcasecmp(cmd, "version")) {
} else if (!strcasecmp(cmd, "maxsamples")) { if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1) { return 0;
result = CPS_BadMaxsamples; } else if (!strcasecmp(cmd, "xleave")) {
done = 1; src->params.interleaved = 1;
} else { } else {
line += n; return 0;
} }
} else {
result = CPS_BadOption;
done = 1;
}
} else {
done = 1;
}
} while (!done);
} }
return result; return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -256,71 +185,6 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
return 1; return 1;
} }
/* ================================================== */
void
CPS_StatusToString(CPS_Status status, char *dest, int len)
{
const char *s = NULL;
if (len > 0)
dest[0] = '\0';
switch (status) {
case CPS_Success:
return;
case CPS_BadOption:
s = "server/peer/pool option";
break;
case CPS_BadHost:
s = "address";
break;
case CPS_BadPort:
s = "port";
break;
case CPS_BadMinpoll:
s = "minpoll";
break;
case CPS_BadMaxpoll:
s = "maxpoll";
break;
case CPS_BadPresend:
s = "presend";
break;
case CPS_BadMaxdelaydevratio:
s = "maxdelaydevratio";
break;
case CPS_BadMaxdelayratio:
s = "maxdelayratio";
break;
case CPS_BadMaxdelay:
s = "maxdelay";
break;
case CPS_BadKey:
s = "key";
break;
case CPS_BadMinstratum:
s = "minstratum";
break;
case CPS_BadPolltarget:
s = "polltarget";
break;
case CPS_BadVersion:
s = "version";
break;
case CPS_BadMaxsources:
s = "maxsources";
break;
case CPS_BadMinsamples:
s = "minsamples";
break;
case CPS_BadMaxsamples:
s = "maxsamples";
break;
}
snprintf(dest, len, "Invalid %s", s);
}
/* ================================================== */ /* ================================================== */

View File

@@ -30,26 +30,6 @@
#include "srcparams.h" #include "srcparams.h"
#include "addressing.h" #include "addressing.h"
typedef enum {
CPS_Success,
CPS_BadOption,
CPS_BadHost,
CPS_BadPort,
CPS_BadMinpoll,
CPS_BadMaxpoll,
CPS_BadPresend,
CPS_BadMaxdelaydevratio,
CPS_BadMaxdelayratio,
CPS_BadMaxdelay,
CPS_BadKey,
CPS_BadMinstratum,
CPS_BadPolltarget,
CPS_BadVersion,
CPS_BadMaxsources,
CPS_BadMinsamples,
CPS_BadMaxsamples,
} CPS_Status;
typedef struct { typedef struct {
char *name; char *name;
unsigned short port; unsigned short port;
@@ -57,14 +37,11 @@ typedef struct {
} CPS_NTP_Source; } CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */ /* Parse a command to add an NTP server or peer */
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Parse a command to enable local reference */ /* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance); extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
/* Get a string describing error status */
extern void CPS_StatusToString(CPS_Status status, char *dest, int len);
/* Remove extra white-space and comments */ /* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line); extern void CPS_NormalizeLine(char *line);

124
conf.c
View File

@@ -57,6 +57,7 @@ static void parse_bindcmdaddress(char *);
static void parse_broadcast(char *); static void parse_broadcast(char *);
static void parse_clientloglimit(char *); static void parse_clientloglimit(char *);
static void parse_fallbackdrift(char *); static void parse_fallbackdrift(char *);
static void parse_hwtimestamp(char *);
static void parse_include(char *); static void parse_include(char *);
static void parse_initstepslew(char *); static void parse_initstepslew(char *);
static void parse_leapsecmode(char *); static void parse_leapsecmode(char *);
@@ -65,8 +66,7 @@ static void parse_log(char *);
static void parse_mailonchange(char *); static void parse_mailonchange(char *);
static void parse_makestep(char *); static void parse_makestep(char *);
static void parse_maxchange(char *); static void parse_maxchange(char *);
static void parse_ratelimit(char *line, int *enabled, int *interval, static void parse_ratelimit(char *line, int *interval, int *burst, int *leak);
int *burst, int *leak);
static void parse_refclock(char *); static void parse_refclock(char *);
static void parse_smoothtime(char *); static void parse_smoothtime(char *);
static void parse_source(char *line, NTP_Source_Type type, int pool); static void parse_source(char *line, NTP_Source_Type type, int pool);
@@ -89,6 +89,7 @@ static double max_drift = 500000.0; /* in ppm */
static double max_slew_rate = 1e6 / 12.0; /* in ppm */ static double max_slew_rate = 1e6 / 12.0; /* in ppm */
static double max_distance = 3.0; static double max_distance = 3.0;
static double max_jitter = 1.0;
static double reselect_distance = 1e-4; static double reselect_distance = 1e-4;
static double stratum_weight = 1e-3; static double stratum_weight = 1e-3;
static double combine_limit = 3.0; static double combine_limit = 3.0;
@@ -147,7 +148,7 @@ static double max_offset;
/* Maximum and minimum number of samples per source */ /* Maximum and minimum number of samples per source */
static int max_samples = 0; /* no limit */ static int max_samples = 0; /* no limit */
static int min_samples = 0; static int min_samples = 6;
/* Threshold for a time adjustment to be logged to syslog */ /* Threshold for a time adjustment to be logged to syslog */
static double log_change_threshold = 1.0; static double log_change_threshold = 1.0;
@@ -181,19 +182,20 @@ static IPAddr bind_cmd_address4, bind_cmd_address6;
/* Path to the Unix domain command socket. */ /* Path to the Unix domain command socket. */
static char *bind_cmd_path; static char *bind_cmd_path;
/* Path to Samba (ntp_signd) socket. */
static char *ntp_signd_socket = NULL;
/* Filename to use for storing pid of running chronyd, to prevent multiple /* Filename to use for storing pid of running chronyd, to prevent multiple
* chronyds being started. */ * chronyds being started. */
static char *pidfile; static char *pidfile;
/* Rate limiting parameters */ /* Rate limiting parameters */
static int ntp_ratelimit_enabled = 0; static int ntp_ratelimit_interval = -10;
static int ntp_ratelimit_interval = 3; static int ntp_ratelimit_burst = 16;
static int ntp_ratelimit_burst = 8; static int ntp_ratelimit_leak = 2;
static int ntp_ratelimit_leak = 3; static int cmd_ratelimit_interval = -10;
static int cmd_ratelimit_enabled = 0;
static int cmd_ratelimit_interval = 1;
static int cmd_ratelimit_burst = 16; static int cmd_ratelimit_burst = 16;
static int cmd_ratelimit_leak = 2; static int cmd_ratelimit_leak = 0;
/* Smoothing constants */ /* Smoothing constants */
static double smooth_max_freq = 0.0; /* in ppm */ static double smooth_max_freq = 0.0; /* in ppm */
@@ -218,6 +220,9 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */ /* Name of the user to which will be dropped root privileges. */
static char *user; static char *user;
/* Array of strings for interfaces with HW timestamping */
static ARR_Instance hwts_interfaces;
typedef struct { typedef struct {
NTP_Source_Type type; NTP_Source_Type type;
int pool; int pool;
@@ -319,6 +324,8 @@ CNF_Initialise(int r)
{ {
restarted = r; restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (char *));
init_sources = ARR_CreateInstance(sizeof (IPAddr)); init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source)); ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters)); refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
@@ -327,11 +334,11 @@ CNF_Initialise(int r)
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
dumpdir = Strdup("."); dumpdir = Strdup("");
logdir = Strdup("."); logdir = Strdup("");
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET); bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup("/var/run/chronyd.pid"); pidfile = Strdup(DEFAULT_PID_FILE);
rtc_device = Strdup("/dev/rtc"); rtc_device = Strdup(DEFAULT_RTC_DEVICE);
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE); hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER); user = Strdup(DEFAULT_USER);
} }
@@ -343,6 +350,10 @@ CNF_Finalise(void)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
Free(*(char **)ARR_GetElement(hwts_interfaces, i));
ARR_DestroyInstance(hwts_interfaces);
for (i = 0; i < ARR_GetSize(ntp_sources); i++) for (i = 0; i < ARR_GetSize(ntp_sources); i++)
Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name); Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name);
@@ -361,6 +372,7 @@ CNF_Finalise(void)
Free(leapsec_tz); Free(leapsec_tz);
Free(logdir); Free(logdir);
Free(bind_cmd_path); Free(bind_cmd_path);
Free(ntp_signd_socket);
Free(pidfile); Free(pidfile);
Free(rtc_device); Free(rtc_device);
Free(rtc_file); Free(rtc_file);
@@ -440,8 +452,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "cmdport")) { } else if (!strcasecmp(command, "cmdport")) {
parse_int(p, &cmd_port); parse_int(p, &cmd_port);
} else if (!strcasecmp(command, "cmdratelimit")) { } else if (!strcasecmp(command, "cmdratelimit")) {
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval, parse_ratelimit(p, &cmd_ratelimit_interval, &cmd_ratelimit_burst, &cmd_ratelimit_leak);
&cmd_ratelimit_burst, &cmd_ratelimit_leak);
} else if (!strcasecmp(command, "combinelimit")) { } else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit); parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "corrtimeratio")) { } else if (!strcasecmp(command, "corrtimeratio")) {
@@ -458,6 +469,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_fallbackdrift(p); parse_fallbackdrift(p);
} else if (!strcasecmp(command, "hwclockfile")) { } else if (!strcasecmp(command, "hwclockfile")) {
parse_string(p, &hwclock_file); parse_string(p, &hwclock_file);
} else if (!strcasecmp(command, "hwtimestamp")) {
parse_hwtimestamp(p);
} else if (!strcasecmp(command, "include")) { } else if (!strcasecmp(command, "include")) {
parse_include(p); parse_include(p);
} else if (!strcasecmp(command, "initstepslew")) { } else if (!strcasecmp(command, "initstepslew")) {
@@ -494,6 +507,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_double(p, &max_distance); parse_double(p, &max_distance);
} else if (!strcasecmp(command, "maxdrift")) { } else if (!strcasecmp(command, "maxdrift")) {
parse_double(p, &max_drift); parse_double(p, &max_drift);
} else if (!strcasecmp(command, "maxjitter")) {
parse_double(p, &max_jitter);
} else if (!strcasecmp(command, "maxsamples")) { } else if (!strcasecmp(command, "maxsamples")) {
parse_int(p, &max_samples); parse_int(p, &max_samples);
} else if (!strcasecmp(command, "maxslewrate")) { } else if (!strcasecmp(command, "maxslewrate")) {
@@ -506,6 +521,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_int(p, &min_sources); parse_int(p, &min_sources);
} else if (!strcasecmp(command, "noclientlog")) { } else if (!strcasecmp(command, "noclientlog")) {
no_client_log = parse_null(p); no_client_log = parse_null(p);
} else if (!strcasecmp(command, "ntpsigndsocket")) {
parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "peer")) { } else if (!strcasecmp(command, "peer")) {
parse_source(p, NTP_PEER, 0); parse_source(p, NTP_PEER, 0);
} else if (!strcasecmp(command, "pidfile")) { } else if (!strcasecmp(command, "pidfile")) {
@@ -515,8 +532,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "port")) { } else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port); parse_int(p, &ntp_port);
} else if (!strcasecmp(command, "ratelimit")) { } else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval, parse_ratelimit(p, &ntp_ratelimit_interval, &ntp_ratelimit_burst, &ntp_ratelimit_leak);
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
} else if (!strcasecmp(command, "refclock")) { } else if (!strcasecmp(command, "refclock")) {
parse_refclock(p); parse_refclock(p);
} else if (!strcasecmp(command, "reselectdist")) { } else if (!strcasecmp(command, "reselectdist")) {
@@ -604,17 +620,13 @@ parse_null(char *line)
static void static void
parse_source(char *line, NTP_Source_Type type, int pool) parse_source(char *line, NTP_Source_Type type, int pool)
{ {
CPS_Status status;
NTP_Source source; NTP_Source source;
char str[64];
source.type = type; source.type = type;
source.pool = pool; source.pool = pool;
status = CPS_ParseNTPSourceAdd(line, &source.params);
if (status != CPS_Success) { if (!CPS_ParseNTPSourceAdd(line, &source.params)) {
CPS_StatusToString(status, str, sizeof (str)); command_parse_error();
other_parse_error(str);
return; return;
} }
@@ -625,13 +637,11 @@ parse_source(char *line, NTP_Source_Type type, int pool)
/* ================================================== */ /* ================================================== */
static void static void
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak) parse_ratelimit(char *line, int *interval, int *burst, int *leak)
{ {
int n, val; int n, val;
char *opt; char *opt;
*enabled = 1;
while (*line) { while (*line) {
opt = line; opt = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
@@ -657,6 +667,7 @@ static void
parse_refclock(char *line) parse_refclock(char *line)
{ {
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options; int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int max_lock_age;
uint32_t ref_id, lock_ref_id; uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion; double offset, delay, precision, max_dispersion;
char *p, *cmd, *name, *param; char *p, *cmd, *name, *param;
@@ -675,6 +686,7 @@ parse_refclock(char *line)
precision = 0.0; precision = 0.0;
max_dispersion = 0.0; max_dispersion = 0.0;
ref_id = 0; ref_id = 0;
max_lock_age = 2;
lock_ref_id = 0; lock_ref_id = 0;
if (!*line) { if (!*line) {
@@ -696,9 +708,9 @@ parse_refclock(char *line)
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
param = Strdup(p); param = Strdup(p);
while (*line) { for (cmd = line; *cmd; line += n, cmd = line) {
cmd = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "refid")) { if (!strcasecmp(cmd, "refid")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1) if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break; break;
@@ -725,6 +737,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "minsamples")) { } else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &min_samples, &n) != 1) if (sscanf(line, "%d%n", &min_samples, &n) != 1)
break; break;
} else if (!strcasecmp(cmd, "maxlockage")) {
if (sscanf(line, "%d%n", &max_lock_age, &n) != 1)
break;
} else if (!strcasecmp(cmd, "maxsamples")) { } else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &max_samples, &n) != 1) if (sscanf(line, "%d%n", &max_samples, &n) != 1)
break; break;
@@ -756,10 +771,9 @@ parse_refclock(char *line)
other_parse_error("Invalid refclock option"); other_parse_error("Invalid refclock option");
return; return;
} }
line += n;
} }
if (*line) { if (*cmd) {
command_parse_error(); command_parse_error();
return; return;
} }
@@ -779,6 +793,7 @@ parse_refclock(char *line)
refclock->precision = precision; refclock->precision = precision;
refclock->max_dispersion = max_dispersion; refclock->max_dispersion = max_dispersion;
refclock->ref_id = ref_id; refclock->ref_id = ref_id;
refclock->max_lock_age = max_lock_age;
refclock->lock_ref_id = lock_ref_id; refclock->lock_ref_id = lock_ref_id;
} }
@@ -1220,6 +1235,15 @@ parse_tempcomp(char *line)
/* ================================================== */ /* ================================================== */
static void
parse_hwtimestamp(char *line)
{
check_number_of_args(line, 1);
*(char **)ARR_GetNewElement(hwts_interfaces) = Strdup(line);
}
/* ================================================== */
static void static void
parse_include(char *line) parse_include(char *line)
{ {
@@ -1246,9 +1270,6 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
{ {
char *dir; char *dir;
UTI_CreateDirAndParents(logdir, 0755, uid, gid);
UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
/* Create a directory for the Unix domain command socket */ /* Create a directory for the Unix domain command socket */
if (bind_cmd_path[0]) { if (bind_cmd_path[0]) {
dir = UTI_PathToDir(bind_cmd_path); dir = UTI_PathToDir(bind_cmd_path);
@@ -1264,6 +1285,11 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
Free(dir); Free(dir);
} }
if (logdir[0])
UTI_CreateDirAndParents(logdir, 0755, uid, gid);
if (dumpdir[0])
UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
} }
/* ================================================== */ /* ================================================== */
@@ -1527,6 +1553,14 @@ CNF_GetMaxDistance(void)
/* ================================================== */ /* ================================================== */
double
CNF_GetMaxJitter(void)
{
return max_jitter;
}
/* ================================================== */
double double
CNF_GetReselectDistance(void) CNF_GetReselectDistance(void)
{ {
@@ -1741,6 +1775,14 @@ CNF_GetBindCommandAddress(int family, IPAddr *addr)
/* ================================================== */ /* ================================================== */
char *
CNF_GetNtpSigndSocket(void)
{
return ntp_signd_socket;
}
/* ================================================== */
char * char *
CNF_GetPidFile(void) CNF_GetPidFile(void)
{ {
@@ -1781,22 +1823,20 @@ CNF_GetLockMemory(void)
/* ================================================== */ /* ================================================== */
int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak) void CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
{ {
*interval = ntp_ratelimit_interval; *interval = ntp_ratelimit_interval;
*burst = ntp_ratelimit_burst; *burst = ntp_ratelimit_burst;
*leak = ntp_ratelimit_leak; *leak = ntp_ratelimit_leak;
return ntp_ratelimit_enabled;
} }
/* ================================================== */ /* ================================================== */
int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak) void CNF_GetCommandRateLimit(int *interval, int *burst, int *leak)
{ {
*interval = cmd_ratelimit_interval; *interval = cmd_ratelimit_interval;
*burst = cmd_ratelimit_burst; *burst = cmd_ratelimit_burst;
*leak = cmd_ratelimit_leak; *leak = cmd_ratelimit_leak;
return cmd_ratelimit_enabled;
} }
/* ================================================== */ /* ================================================== */
@@ -1878,3 +1918,11 @@ CNF_GetInitStepThreshold(void)
{ {
return init_slew_threshold; return init_slew_threshold;
} }
/* ================================================== */
ARR_Instance
CNF_GetHwTsInterfaces(void)
{
return hwts_interfaces;
}

9
conf.h
View File

@@ -29,6 +29,7 @@
#define GOT_CONF_H #define GOT_CONF_H
#include "addressing.h" #include "addressing.h"
#include "array.h"
#include "reference.h" #include "reference.h"
extern void CNF_Initialise(int restarted); extern void CNF_Initialise(int restarted);
@@ -76,6 +77,7 @@ extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr); extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindCommandPath(void); extern char *CNF_GetBindCommandPath(void);
extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void); extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void); extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void); extern char *CNF_GetLeapSecTimezone(void);
@@ -88,6 +90,7 @@ extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void); extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetMaxDistance(void); extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void); extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void); extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void); extern double CNF_GetCombineLimit(void);
@@ -99,8 +102,8 @@ extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void); extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void); extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak); extern void CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak); extern void CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only); extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2); extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
@@ -117,4 +120,6 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void); extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void); extern double CNF_GetInitStepThreshold(void);
extern ARR_Instance CNF_GetHwTsInterfaces(void);
#endif /* GOT_CONF_H */ #endif /* GOT_CONF_H */

136
configure vendored
View File

@@ -24,6 +24,7 @@ test_code () {
printf "%s" "Checking for $name : " printf "%s" "Checking for $name : "
( (
echo "#include \"config.h\""
for h in $headers; do for h in $headers; do
echo "#include <$h>" echo "#include <$h>"
done done
@@ -79,9 +80,8 @@ For better control, use the options below.
--disable-readline Disable line editing support --disable-readline Disable line editing support
--without-readline Don't use GNU readline even if it is available --without-readline Don't use GNU readline even if it is available
--without-editline Don't use editline even if it is available --without-editline Don't use editline even if it is available
--readline-dir=DIR Specify parent of readline include and lib directories --with-readline-includes=DIR Specify where readline include directory is
--readline-inc-dir=DIR Specify where readline include directory is --with-readline-library=DIR Specify where readline lib directory is
--readline-lib-dir=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is --with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5 --disable-sechash Disable support for hashes other than MD5
--without-nss Don't use NSS even if it is available --without-nss Don't use NSS even if it is available
@@ -99,10 +99,15 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available --without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving --disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error --disable-forcednsretry Don't retry on permanent DNS error
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds --with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago] since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root] --with-user=USER Specify default chronyd user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file --with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail] --with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support --enable-debug Enable debugging support
@@ -114,7 +119,7 @@ Fine tuning of the installation directories:
--mandir=DIR man documentation [DATAROOTDIR/man] --mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/chrony] --docdir=DIR documentation root [DATAROOTDIR/doc/chrony]
--localstatedir=DIR modifiable single-machine data [/var] --localstatedir=DIR modifiable single-machine data [/var]
--chronysockdir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony] --chronyrundir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony]
--chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony] --chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony]
Overriding system detection when cross-compiling: Overriding system detection when cross-compiling:
@@ -213,9 +218,16 @@ try_setsched=0
try_lockmem=0 try_lockmem=0
feat_asyncdns=1 feat_asyncdns=1
feat_forcednsretry=1 feat_forcednsretry=1
try_clock_gettime=1
try_recvmmsg=1
feat_timestamping=1
try_timestamping=0
feat_ntp_signd=0
ntp_era_split="" ntp_era_split=""
default_user="root" default_user="root"
default_hwclockfile="" default_hwclockfile=""
default_pidfile="/var/run/chronyd.pid"
default_rtcdevice="/dev/rtc"
mail_program="/usr/lib/sendmail" mail_program="/usr/lib/sendmail"
for option for option
@@ -269,8 +281,8 @@ do
--localstatedir=* ) --localstatedir=* )
SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'` SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--chronysockdir=* ) --chronyrundir=* | --chronysockdir=* )
SETCHRONYSOCKDIR=`echo $option | sed -e 's/^.*=//;'` SETCHRONYRUNDIR=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--chronyvardir=* ) --chronyvardir=* )
SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'` SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'`
@@ -317,6 +329,15 @@ do
--disable-forcednsretry) --disable-forcednsretry)
feat_forcednsretry=0 feat_forcednsretry=0
;; ;;
--without-clock-gettime)
try_clock_gettime=0
;;
--disable-timestamping)
feat_timestamping=0
;;
--enable-ntp-signd)
feat_ntp_signd=1
;;
--with-ntp-era=* ) --with-ntp-era=* )
ntp_era_split=`echo $option | sed -e 's/^.*=//;'` ntp_era_split=`echo $option | sed -e 's/^.*=//;'`
;; ;;
@@ -326,6 +347,12 @@ do
--with-hwclockfile=* ) --with-hwclockfile=* )
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'` default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--with-pidfile=* )
default_pidfile=`echo $option | sed -e 's/^.*=//;'`
;;
--with-rtcdevice=* )
default_rtcdevice=`echo $option | sed -e 's/^.*=//;'`
;;
--with-sendmail=* ) --with-sendmail=* )
mail_program=`echo $option | sed -e 's/^.*=//;'` mail_program=`echo $option | sed -e 's/^.*=//;'`
;; ;;
@@ -366,6 +393,7 @@ case $OPERATINGSYSTEM in
[ $try_libcap != "0" ] && try_libcap=1 [ $try_libcap != "0" ] && try_libcap=1
try_rtc=1 try_rtc=1
[ $try_seccomp != "0" ] && try_seccomp=1 [ $try_seccomp != "0" ] && try_seccomp=1
try_timestamping=1
try_setsched=1 try_setsched=1
try_lockmem=1 try_lockmem=1
try_phc=1 try_phc=1
@@ -373,6 +401,9 @@ case $OPERATINGSYSTEM in
echo "Configuring for " $SYSTEM echo "Configuring for " $SYSTEM
;; ;;
FreeBSD) FreeBSD)
# 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"
add_def FREEBSD add_def FREEBSD
if [ $feat_droproot = "1" ]; then if [ $feat_droproot = "1" ]; then
@@ -396,7 +427,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME SETTIME BINDSOCKET" priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
fi fi
echo "Configuring for MacOS X (" $SYSTEM "MacOS X version" $VERSION ")" echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
;; ;;
SunOS) SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o" EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
@@ -432,8 +463,13 @@ fi
if [ $feat_ntp = "1" ]; then if [ $feat_ntp = "1" ]; then
add_def FEAT_NTP add_def FEAT_NTP
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o" EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o"
if [ $feat_ntp_signd = "1" ]; then
add_def FEAT_SIGND
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
fi
else else
feat_asyncdns=0 feat_asyncdns=0
feat_timestamping=0
fi fi
if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then
@@ -559,6 +595,21 @@ then
fi fi
fi fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
fi
fi
fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \ if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
'return getaddrinfo(0, 0, 0, 0);' 'return getaddrinfo(0, 0, 0, 0);'
then then
@@ -579,6 +630,35 @@ if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; the
add_def HAVE_ARC4RANDOM add_def HAVE_ARC4RANDOM
fi fi
RECVMMSG_CODE='
struct mmsghdr hdr;
return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
if [ $try_recvmmsg = "1" ]; then
if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
add_def HAVE_RECVMMSG
else
if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
"$EXTRA_LIBS" "$RECVMMSG_CODE"
then
add_def _GNU_SOURCE
add_def HAVE_RECVMMSG
fi
fi
fi
if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
test_code 'SW/HW timestamping' 'sys/types.h sys/socket.h linux/net_tstamp.h
linux/errqueue.h linux/ptp_clock.h' '' '' '
int val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG;
return sizeof (struct scm_timestamping) + SCM_TSTAMP_SND + PTP_SYS_OFFSET +
setsockopt(0, SOL_SOCKET, SO_SELECT_ERR_QUEUE + SO_TIMESTAMPING,
&val, sizeof (val));'
then
add_def HAVE_LINUX_TIMESTAMPING
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o"
fi
timepps_h="" timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
@@ -649,19 +729,11 @@ then
fi fi
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \ 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' '' '' \ test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
'ioctl(1, PTP_CLOCK_GETCAPS, 0);' 'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
then then
if test_code 'clock_gettime()' 'time.h' '' '' 'clock_gettime(0, NULL);'; then add_def FEAT_PHC
add_def FEAT_PHC
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(0, NULL);'
then
EXTRA_LIBS="$EXTRA_LIBS -lrt"
add_def FEAT_PHC
fi
fi
fi fi
if [ $try_setsched = "1" ] && \ if [ $try_setsched = "1" ] && \
@@ -691,7 +763,6 @@ then
add_def FORCE_DNSRETRY add_def FORCE_DNSRETRY
fi fi
READLINE_COMPILE=""
READLINE_LINK="" READLINE_LINK=""
if [ $feat_readline = "1" ]; then if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then if [ $try_editline = "1" ]; then
@@ -701,7 +772,7 @@ if [ $feat_readline = "1" ]; then
then then
add_def FEAT_READLINE add_def FEAT_READLINE
add_def USE_EDITLINE add_def USE_EDITLINE
READLINE_COMPILE="$readline_inc" MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -ledit" READLINE_LINK="$readline_lib -ledit"
fi fi
fi fi
@@ -712,7 +783,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));' 'add_history(readline("prompt"));'
then then
add_def FEAT_READLINE add_def FEAT_READLINE
READLINE_COMPILE="$readline_inc" MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -lreadline" READLINE_LINK="$readline_lib -lreadline"
fi fi
fi fi
@@ -724,7 +795,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));' 'add_history(readline("prompt"));'
then then
add_def FEAT_READLINE add_def FEAT_READLINE
READLINE_COMPILE="$readline_inc" MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses" READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi fi
fi fi
@@ -733,7 +804,6 @@ if [ $feat_readline = "1" ]; then
fi fi
HASH_OBJ="hash_intmd5.o" HASH_OBJ="hash_intmd5.o"
HASH_COMPILE=""
HASH_LINK="" HASH_LINK=""
if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
@@ -744,9 +814,9 @@ if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));' 'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
then then
HASH_OBJ="hash_nss.o" HASH_OBJ="hash_nss.o"
HASH_COMPILE="$test_cflags"
HASH_LINK="$test_link" HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH add_def FEAT_SECHASH
fi fi
fi fi
@@ -756,9 +826,9 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);' 'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
then then
HASH_OBJ="hash_tomcrypt.o" HASH_OBJ="hash_tomcrypt.o"
HASH_COMPILE="-I/usr/include/tomcrypt"
HASH_LINK="-ltomcrypt" HASH_LINK="-ltomcrypt"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/tomcrypt"
add_def FEAT_SECHASH add_def FEAT_SECHASH
fi fi
fi fi
@@ -808,9 +878,9 @@ if [ "x$SETLOCALSTATEDIR" != "x" ]; then
LOCALSTATEDIR=$SETLOCALSTATEDIR LOCALSTATEDIR=$SETLOCALSTATEDIR
fi fi
CHRONYSOCKDIR=${LOCALSTATEDIR}/run/chrony CHRONYRUNDIR=${LOCALSTATEDIR}/run/chrony
if [ "x$SETCHRONYSOCKDIR" != "x" ]; then if [ "x$SETCHRONYRUNDIR" != "x" ]; then
CHRONYSOCKDIR=$SETCHRONYSOCKDIR CHRONYRUNDIR=$SETCHRONYRUNDIR
fi fi
CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony
@@ -820,13 +890,15 @@ fi
add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\"" add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\""
add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\"" add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
add_def DEFAULT_PID_FILE "\"$default_pidfile\""
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
add_def DEFAULT_USER "\"$default_user\"" add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYSOCKDIR/chronyd.sock\"" add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\"" add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features IPV6 DEBUG`" common_features="`get_features IPV6 DEBUG`"
chronyc_features="`get_features READLINE`" chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH ASYNCDNS`" chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH SIGND ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\"" add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\"" add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features" echo "Features : $chronyd_features $chronyc_features $common_features"
@@ -850,18 +922,18 @@ do
s%@LDFLAGS@%${MYLDFLAGS}%;\ s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\ s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\ s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
s%@HASH_OBJ@%${HASH_OBJ}%;\ s%@HASH_OBJ@%${HASH_OBJ}%;\
s%@HASH_COMPILE@%${HASH_COMPILE}%;\
s%@SYSCONFDIR@%${SYSCONFDIR}%;\ s%@SYSCONFDIR@%${SYSCONFDIR}%;\
s%@BINDIR@%${BINDIR}%;\ s%@BINDIR@%${BINDIR}%;\
s%@SBINDIR@%${SBINDIR}%;\ s%@SBINDIR@%${SBINDIR}%;\
s%@DOCDIR@%${DOCDIR}%;\ s%@DOCDIR@%${DOCDIR}%;\
s%@MANDIR@%${MANDIR}%;\ s%@MANDIR@%${MANDIR}%;\
s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\ s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\
s%@CHRONYSOCKDIR@%${CHRONYSOCKDIR}%;\ s%@CHRONYRUNDIR@%${CHRONYRUNDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\ s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\ s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
s%@DEFAULT_USER@%${default_user}%;\ s%@DEFAULT_USER@%${default_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \ s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f < ${f}.in > $f

View File

@@ -1,16 +1,16 @@
Notes for installing chrony on MacOS X Notes for installing chrony on macOS
Author: Bryan Christianson (bryan@whatroute.net) Author: Bryan Christianson (bryan@whatroute.net)
------------------------------------------------ ------------------------------------------------
These files are for those admins/users who would prefer to install chrony These files are for those admins/users who would prefer to install chrony
from the source distribution and are intended as guidelines rather than from the source distribution and are intended as guidelines rather than
being definitive. They can be edited with a plain text editor, such as being definitive. They can be edited with a plain text editor, such as
vi, emacs or your favourite IDE (xcode) vi, emacs or your favourite IDE (Xcode)
It is assumed you are comfortable with installing software from the It is assumed you are comfortable with installing software from the
terminal command line and know how to use sudo to acquire root access. terminal command line and know how to use sudo to acquire root access.
If you are not familiar with the MacOS X command line then If you are not familiar with the macOS command line then
please consider using ChronyControl from http://whatroute.net/chronycontrol.html please consider using ChronyControl from http://whatroute.net/chronycontrol.html
ChronyControl provides a gui wrapper for installing these files and sets the ChronyControl provides a gui wrapper for installing these files and sets the
@@ -72,7 +72,7 @@ Installing the support files
1. chronylogrotate.sh 1. chronylogrotate.sh
This is a simple shell script that deletes old log files. Unfortunately because This is a simple shell script that deletes old log files. Unfortunately because
of the need to run chronyc, the standard MacOS X logrotation does not work with of the need to run chronyc, the standard macOS logrotation does not work with
chrony logs. chrony logs.
This script runs on a daily basis under control of launchd and should be This script runs on a daily basis under control of launchd and should be

View File

@@ -13,19 +13,23 @@ BINDIR = @BINDIR@
SBINDIR = @SBINDIR@ SBINDIR = @SBINDIR@
MANDIR = @MANDIR@ MANDIR = @MANDIR@
DOCDIR = @DOCDIR@ DOCDIR = @DOCDIR@
CHRONYSOCKDIR = @CHRONYSOCKDIR@ CHRONYRUNDIR = @CHRONYRUNDIR@
CHRONYVARDIR = @CHRONYVARDIR@ CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@ CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@ DEFAULT_USER = @DEFAULT_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@ DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\ SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
s%\@BINDIR\@%$(BINDIR)%g;\ s%\@BINDIR\@%$(BINDIR)%g;\
s%\@SBINDIR\@%$(SBINDIR)%g;\ s%\@SBINDIR\@%$(SBINDIR)%g;\
s%\@CHRONY_VERSION\@%$(CHRONY_VERSION)%g;\ s%\@CHRONY_VERSION\@%$(CHRONY_VERSION)%g;\
s%\@DEFAULT_HWCLOCK_FILE\@%$(DEFAULT_HWCLOCK_FILE)%g;\ s%\@DEFAULT_HWCLOCK_FILE\@%$(DEFAULT_HWCLOCK_FILE)%g;\
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\ s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
s%\@CHRONYSOCKDIR\@%$(CHRONYSOCKDIR)%g;\ s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;" s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
man: $(MAN_FILES) $(MAN_IN_FILES) man: $(MAN_FILES) $(MAN_IN_FILES)

View File

@@ -110,12 +110,20 @@ round-trip delay of 0.3 seconds or more should be ignored. The default value is
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 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 buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected. maxdelayratio times the minimum delay, it will be rejected. This option works
only in the *server* directive when not in the interleaved mode.
*maxdelaydevratio* _ratio_::: *maxdelaydevratio* _ratio_:::
If a measurement has a ratio of the increase in the round-trip delay from the 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 minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0. rejected. The default is 10.0.
*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
known asymmetry in network delay or timestamping errors. For example, if
packets sent to the source were on average delayed by 100 microseconds more
than packets sent from the source back, the correction would be -0.00005 (-50
microseconds). The default is 0.0.
*minsamples* _samples_::: *minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive. <<minsamples,*minsamples*>> directive.
@@ -149,11 +157,29 @@ clock. Together with the *trust* option this might be useful to allow a trusted
authenticated source to be safely combined with unauthenticated sources in authenticated source to be safely combined with unauthenticated sources in
order to improve the accuracy of the clock. They can be selected and used for 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. synchronisation only if they agree with the trusted and required source.
*xleave*:::
This option enables an interleaved mode which allows the server or the peer to
send transmit timestamps captured after the actual transmission (e.g. when the
server or the peer is running *chronyd* with HW timestamping enabled by the
<<hwtimestamp,*hwtimestamp*>> directive). This can significantly improve the
accuracy of the measurements.
+
The interleaved mode is compatible with servers that support only the basic
mode, but peers must both support and have enabled the interleaved mode,
otherwise the synchronisation will work only in one direction. Note that even
servers that support the interleaved mode might respond in the basic mode as
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.
*polltarget* _target_::: *polltarget* _target_:::
Target number of measurements to use for the regression algorithm which Target number of measurements to use for the regression algorithm which
*chronyd* will try to maintain by adjusting the polling interval between *chronyd* will try to maintain by adjusting the polling interval between
*minpoll* and *maxpoll*. A higher target makes *chronyd* prefer shorter polling *minpoll* and *maxpoll*. A higher target makes *chronyd* prefer shorter polling
intervals. The default is 6 and a useful range is from 6 to 60. intervals. The default is 8 and a useful range is from 6 to 60.
*port* _port_::: *port* _port_:::
This option allows the UDP port on which the server understands NTP requests to This option allows the UDP port on which the server understands NTP requests to
be specified. For normal servers this option should not be required (the be specified. For normal servers this option should not be required (the
@@ -177,8 +203,11 @@ presend 9
---- ----
+ +
when the polling interval is 512 seconds or more, an extra NTP client packet when the polling interval is 512 seconds or more, an extra NTP client packet
will be sent to the server a short time (4 seconds) before making the actual will be sent to the server a short time (2 seconds) before making the actual
measurement. measurement.
+
The *presend* option cannot be used in the *peer* directive. If it is used
with the *xleave* option, *chronyd* will send two extra packets instead of one.
*minstratum* _stratum_::: *minstratum* _stratum_:::
When the synchronisation source is selected from available sources, sources When the synchronisation source is selected from available sources, sources
with lower stratum are normally slightly preferred. This option can be used to with lower stratum are normally slightly preferred. This option can be used to
@@ -187,9 +216,13 @@ avoid selecting that source. This is useful with low stratum sources that are
known to be unreliable or inaccurate and which should be used only when other known to be unreliable or inaccurate and which should be used only when other
sources are unreachable. sources are unreachable.
*version* _version_::: *version* _version_:::
This option sets the NTP version number used in packets sent to the server. This option sets the NTP version of packets sent to the server. This can be
This can be useful when the server runs an old NTP implementation that does not useful when the server runs an old NTP implementation that does not respond to
respond to newer versions. The default version number is 4. requests using a newer version. The default version depends on whether a key is
specified by the *key* option and which authentication hash function the key
is using. If the output size of the hash function is longer than 160 bits, the
default version is 3 for compatibility with older *chronyd* servers. Otherwise,
the default version is 4.
[[pool]]*pool* _name_ [_option_]...:: [[pool]]*pool* _name_ [_option_]...::
The syntax of this directive is similar to that for the <<server,*server*>> The syntax of this directive is similar to that for the <<server,*server*>>
@@ -218,26 +251,33 @@ pool pool.ntp.org iburst maxsources 3
[[peer]]*peer* _hostname_ [_option_]...:: [[peer]]*peer* _hostname_ [_option_]...::
The syntax of this directive is identical to that for the <<server,*server*>> The syntax of this directive is identical to that for the <<server,*server*>>
directive, except that it is used to specify an NTP peer rather than an NTP directive, except that it specifies a symmetric association with an NTP peer
server. instead of a client/server association with an NTP server. A single symmetric
association allows the peers to be both servers and clients to each other. This
is mainly useful when the NTP implementation of the peer (e.g. *ntpd*) supports
ephemeral symmetric associations and does not need to be configured with an
address of this host. *chronyd* does not support ephemeral associations.
+ +
When a key is specified by the *key* option to enable authentication, both When a key is specified by the *key* option to enable authentication, both
peers must be configured to use the same key and the same key number. peers must use the same key and the same key number.
+ +
Please note that NTP peers that are not configured with a key to enable Note that the symmetric mode is less secure than the client/server mode. A
authentication are vulnerable to a denial-of-service attack. An attacker denial-of-service attack is possible on unauthenticated symmetric associations,
knowing that NTP hosts A and B are peering with each other can send a packet i.e. when the peer was specified without the *key* option. An attacker who does
with random timestamps to host A with source address of B which will set the not see network traffic between two hosts, but knows that they are peering with
NTP state variables on A to the values sent by the attacker. Host A will then each other, can periodically send them unauthenticated packets with spoofed
send on its next poll to B a packet with an origin timestamp that does not match source addresses in order to disrupt their NTP state and prevent them from
the transmit timestamp of B and the packet will be dropped. If the attacker synchronising to each other. When the association is authenticated, an attacker
does this periodically for both hosts, they will not be able to synchronise to who does see the network traffic, but cannot prevent the packets from reaching
each other. the other host, can still disrupt the state by replaying old packets. The
+ attacker has effectively the same power as a man-in-the-middle attacker. A
This attack can be prevented by enabling authentication with the *key* option, partial protection against this attack is implemented in *chronyd*, which can
or by using the <<server,*server*>> directive on both sides to specify the other protect the peers if they are using the same polling interval and they never
host as a server instead of a peer. The disadvantage of the *server* directive sent an authenticated packet with a timestamp from future, but it should not be
is that it will double the network traffic between the two hosts. relied on as it is difficult to ensure the conditions are met. If two hosts
should be able to synchronise to each other in both directions, it is
recommended to use two separate client/server associations (specified by the
<<server,*server*>> directive on both hosts) instead.
[[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...:: [[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...::
In normal operation, *chronyd* slews the time when it needs to adjust the In normal operation, *chronyd* slews the time when it needs to adjust the
@@ -395,6 +435,11 @@ This option sets the rate of the pulses in the PPS signal (in Hz). This option
controls how the pulses will be completed with real time. To actually receive controls how the pulses will be completed with real time. To actually receive
more than one pulse per second, a negative *dpoll* has to be specified (-3 for more than one pulse per second, a negative *dpoll* has to be specified (-3 for
a 5Hz signal). The default is 1. a 5Hz signal). The default is 1.
*maxlockage* _pulses_:::
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.
*offset* _offset_::: *offset* _offset_:::
This option can be used to compensate for a constant error. The specified 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. offset (in seconds) is applied to all samples produced by the reference clock.
@@ -506,12 +551,12 @@ saved.
An example of the directive is: An example of the directive is:
+ +
---- ----
dumpdir @CHRONYVARDIR@ dumpdir @CHRONYRUNDIR@
---- ----
+ +
A source whose reference ID (the IP address for IPv4 sources) is _1.2.3.4_ A source whose IP address is _1.2.3.4_ would have its measurement history saved
would have its measurement history saved in the file in the file _@CHRONYRUNDIR@/1.2.3.4.dat_. History of reference clocks is saved
_/var/lib/chrony/1.2.3.4.dat_. to files named by their reference ID in form of _refid:XXXXXXXX.dat_.
[[dumponexit]]*dumponexit*:: [[dumponexit]]*dumponexit*::
If this directive is present, it indicates that *chronyd* should save the If this directive is present, it indicates that *chronyd* should save the
@@ -529,7 +574,7 @@ useful range is 4 to 64.
The *minsamples* directive sets the default minimum number of samples that The *minsamples* directive sets the default minimum number of samples that
*chronyd* should keep for each source. This setting can be overridden for *chronyd* should keep for each source. This setting can be overridden for
individual sources in the <<server,*server*>> and <<refclock,*refclock*>> individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
directives. The default value is 0. The useful range is 4 to 64. directives. The default value is 6. The useful range is 4 to 64.
=== Source selection === Source selection
@@ -561,6 +606,13 @@ Setting *maxdistance* to a larger value can be useful to allow synchronisation
with a server that only has a very infrequent connection to its sources and can with a server that only has a very infrequent connection to its sources and can
accumulate a large dispersion between updates of its clock. accumulate a large dispersion between updates of its clock.
[[maxjitter]]*maxjitter* _jitter_::
The *maxjitter* directive sets the maximum allowed jitter of the sources to not
be rejected by the source selection algorithm. This prevents synchronisation
with sources that have a small root distance, but their time is too variable.
+
By default, the maximum jitter is 1 second.
[[minsources]]*minsources* _sources_:: [[minsources]]*minsources* _sources_::
The *minsources* directive sets the minimum number of sources that need to be The *minsources* directive sets the minimum number of sources that need to be
considered as selectable in the source selection algorithm before the local considered as selectable in the source selection algorithm before the local
@@ -1008,25 +1060,20 @@ There is also a *deny all* directive with similar behaviour to the *allow all*
directive. directive.
[[bindaddress]]*bindaddress* _address_:: [[bindaddress]]*bindaddress* _address_::
The *bindaddress* directive allows you to restrict the network interface to The *bindaddress* directive binds the socket on which *chronyd* listens for NTP
which *chronyd* will listen for NTP requests. This provides an additional level requests to a local address of the computer. On systems other than Linux, the
of access restriction above that available through the <<deny,*deny*>> address of the computer needs to be already configured when *chronyd* is
mechanism. started.
+ +
Suppose you have a local network with addresses in the _192.168.1.0_ An example of the use of the directive is:
subnet together with an Internet connection. The network interface's IP
address is _192.168.1.1_. Suppose you want to block all access through the
Internet connection. You could add the line:
+ +
---- ----
bindaddress 192.168.1.1 bindaddress 192.168.1.1
---- ----
+ +
to the configuration file. Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
+ directive can be specified. Therefore, it is not useful on computers which
For each of the IPv4 and IPv6 protocols, only one *bindaddress* directive can be should serve NTP on multiple network interfaces.
specified. Therefore, it is not useful on computers which should serve NTP on
multiple network interfaces.
[[broadcast]]*broadcast* _interval_ _address_ [_port_]:: [[broadcast]]*broadcast* _interval_ _address_ [_port_]::
The *broadcast* directive is used to declare a broadcast address to which The *broadcast* directive is used to declare a broadcast address to which
@@ -1065,8 +1112,10 @@ directive.
[[clientloglimit]]*clientloglimit* _limit_:: [[clientloglimit]]*clientloglimit* _limit_::
This directive specifies the maximum amount of memory that *chronyd* is allowed This directive specifies the maximum amount of memory that *chronyd* is allowed
to allocate for logging of client accesses. The default limit is 524288 bytes, to allocate for logging of client accesses and the state that *chronyd* as an
which allows monitoring of several thousands of addresses at the same time. NTP server needs to support the interleaved mode for its clients. The default
limit is 524288 bytes, which is sufficient for monitoring about four thousand
clients at the same time.
+ +
In older *chrony* versions if the limit was set to 0, the memory allocation was In older *chrony* versions if the limit was set to 0, the memory allocation was
unlimited. unlimited.
@@ -1080,7 +1129,8 @@ clientloglimit 1048576
[[noclientlog]]*noclientlog*:: [[noclientlog]]*noclientlog*::
This directive, which takes no arguments, specifies that client accesses are This directive, which takes no arguments, specifies that client accesses are
not to be logged. Normally they are logged, allowing statistics to be reported not to be logged. Normally they are logged, allowing statistics to be reported
using the <<chronyc.adoc#clients,*clients*>> command in *chronyc*. using the <<chronyc.adoc#clients,*clients*>> command in *chronyc*. This option
also effectively disables server support for the NTP interleaved mode.
[[local]]*local* [_option_]...:: [[local]]*local* [_option_]...::
The *local* directive enables a local reference mode, which allows *chronyd* The *local* directive enables a local reference mode, which allows *chronyd*
@@ -1142,6 +1192,23 @@ An example of the directive is:
local stratum 10 orphan local stratum 10 orphan
---- ----
[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
This directive specifies the location of the Samba *ntp_signd* socket when it
is running as a Domain Controller (DC). If *chronyd* is compiled with this
feature, responses to MS-SNTP clients will be signed by the *smbd* daemon.
+
Note that MS-SNTP requests are not authenticated and any client that is allowed
to access the server by the <<allow,*allow*>> directive, or the
<<chronyc.adoc#allow,*allow*>> command in *chronyc*, can get an MS-SNTP
response signed with a trust account's password and try to crack the password
in a brute-force attack. Access to the server should be carefully controlled.
+
An example of the directive is:
+
----
ntpsigndsocket /var/lib/samba/ntp_signd
----
[[port]]*port* _port_:: [[port]]*port* _port_::
This option allows you to configure the port on which *chronyd* will listen for This option allows you to configure the port on which *chronyd* will listen for
NTP requests. The port will be open only when an address is allowed by the NTP requests. The port will be open only when an address is allowed by the
@@ -1154,8 +1221,8 @@ source port used in NTP client requests can be set by the
<<acquisitionport,*acquisitionport*>> directive. <<acquisitionport,*acquisitionport*>> directive.
[[ratelimit]]*ratelimit* [_option_]...:: [[ratelimit]]*ratelimit* [_option_]...::
This directive enables response rate limiting for NTP packets. Its purpose is This directive configures response rate limiting for NTP packets. Its purpose
to reduce network traffic with misconfigured or broken NTP clients that are is to reduce network traffic with misconfigured or broken NTP clients that are
polling the server too frequently. The limits are applied to individual IP polling the server too frequently. The limits are applied to individual IP
addresses. If multiple clients share one IP address (e.g. multiple hosts behind addresses. If multiple clients share one IP address (e.g. multiple hosts behind
NAT), the sum of their traffic will be limited. If a client that increases its NAT), the sum of their traffic will be limited. If a client that increases its
@@ -1170,33 +1237,40 @@ in any order):
+ +
*interval*::: *interval*:::
This option sets the minimum interval between responses. It is defined as a This option sets the minimum interval between responses. It is defined as a
power of 2 in seconds. The default value is 3 (8 seconds). The minimum value power of 2 in seconds. The default value is -10 (1/1024 of a second, or 1024
is -4 and the maximum value is 12. packets per second). The minimum value is -19 (524288 packets per second) and
the maximum value is 12 (one packet per 4096 seconds). Note that with values
below -4 the rate limiting is coarse (responses are allowed in bursts, even if
the interval between them is shorter than the specified interval).
*burst*::: *burst*:::
This option sets the maximum number of responses that can be sent in a burst, This option sets the maximum number of responses that can be sent in a burst,
temporarily exceeding the limit specified by the *interval* option. This is temporarily exceeding the limit specified by the *interval* option. This is
useful for clients that make rapid measurements on start (e.g. *chronyd* with useful for clients that make rapid measurements on start (e.g. *chronyd* with
the *iburst* option). The default value is 8. The minimum value is 1 and the the *iburst* option). The default value is 16. The minimum value is 1 and the
maximum value is 255. maximum value is 255.
*leak*::: *leak*:::
This option sets the rate at which responses are randomly allowed even if the This option sets the rate at which responses are randomly allowed even if the
limits specified by the *interval* and *burst* options are exceeded. This is limits specified by the *interval* and *burst* options are exceeded. This is
necessary to prevent an attacker who is sending requests with a spoofed necessary to prevent an attacker who is sending requests with a spoofed
source address from completely blocking responses to that address. The leak source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 3 by default, i.e. on average at rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every eighth request has a response. The minimum value is 1 and the least every fourth request has a response. The minimum value is 0, which
maximum value is 4. disables the rate limiting, and the maximum value is 4 (one response per 16
requests).
:: ::
+ +
An example use of the directive is: An example use of the directive is:
+ +
---- ----
ratelimit interval 4 burst 4 ratelimit interval 1 burst 8
---- ----
+ +
This would reduce the response rate for IP addresses that send packets on This would reduce the response rate for IP addresses sending packets on average
average more frequently than once per 16 seconds or send packets in bursts more than once per 2 seconds, or sending packets in bursts of more than 8
of more than 4 packets. packets, by up to 75% (with default *leak* of 2).
+
Rate limiting can be disabled by setting the *leak* option to 0, or by the
<<noclientlog,*noclientlog*>> directive.
[[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]:: [[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
The *smoothtime* directive can be used to enable smoothing of the time that The *smoothtime* directive can be used to enable smoothing of the time that
@@ -1251,16 +1325,17 @@ smoothtime 50000 0.01
=== Command and monitoring access === Command and monitoring access
[[bindcmdaddress]]*bindcmdaddress* _address_:: [[bindcmdaddress]]*bindcmdaddress* _address_::
The *bindcmdaddress* directive allows you to specify the network interface on The *bindcmdaddress* directive allows you to specify an IP address of an
which *chronyd* will listen for monitoring command packets (issued by interface on which *chronyd* will listen for monitoring command packets (issued
*chronyc*). This provides an additional level of access restriction above that by *chronyc*). On systems other than Linux, the address of the interface needs
available through the <<cmddeny,*cmddeny*>> mechanism. to be already configured when *chronyd* is started.
+ +
This directive can also change the path of the Unix domain command socket, This directive can also change the path of the Unix domain command socket,
which is used by *chronyc* to send configuration commands. The socket must be which is used by *chronyc* to send configuration commands. The socket must be
in a directory that is accessible only by the root or _chrony_ user. The in a directory that is accessible only by the root or _chrony_ user. The
directory will be created on start if it does not exist. The compiled-in default directory will be created on start if it does not exist. The compiled-in default
path of the socket is _@CHRONYSOCKDIR@/chronyd.sock_. path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be
disabled by setting the path to _/_.
+ +
By default, *chronyd* binds to the loopback interface (with addresses By default, *chronyd* binds to the loopback interface (with addresses
_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen _127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen
@@ -1273,8 +1348,8 @@ bindcmdaddress ::
+ +
to the configuration file. to the configuration file.
+ +
For each of the IPv4 and IPv6 protocols, only one *bindcmdaddress* directive can be For each of the IPv4, IPv6, and Unix domain protocols, only one
specified. *bindcmdaddress* directive can be specified.
+ +
An example that sets the path of the Unix domain command socket is: An example that sets the path of the Unix domain command socket is:
+ +
@@ -1322,16 +1397,18 @@ This would make *chronyd* use UDP 257 as its command port. (*chronyc* would
need to be run with the *-p 257* switch to inter-operate correctly.) need to be run with the *-p 257* switch to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...:: [[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is This directive is identical to the <<ratelimit,*ratelimit*>> directive, except
similar to the <<ratelimit,*ratelimit*>> directive, except responses to it configures rate limiting for command packets and responses to localhost are
localhost are never limited and the default interval is 1 (2 seconds), the default never limited. It is disabled by default (the default *leak* is 0).
burst is 16, and the default leak rate is 2.
+ +
An example of the use of the directive is: An example of the use of the directive is:
+ +
---- ----
cmdratelimit interval 2 cmdratelimit interval -2 burst 128 leak 2
---- ----
+
This would reduce response rate for addresses that send more than 4 requests
per second, or bursts of more than 128 packets, by up to 75%.
=== Real-time clock (RTC) === Real-time clock (RTC)
@@ -1368,7 +1445,7 @@ This would set the threshold error to 30 seconds.
[[rtcdevice]]*rtcdevice* _device_:: [[rtcdevice]]*rtcdevice* _device_::
The *rtcdevice* directive sets the path to the device file for accessing the The *rtcdevice* directive sets the path to the device file for accessing the
RTC. The default path is _/dev/rtc_. RTC. The default path is _@DEFAULT_RTC_DEVICE@_.
[[rtcfile]]*rtcfile* _file_:: [[rtcfile]]*rtcfile* _file_::
The *rtcfile* directive defines the name of the file in which *chronyd* can The *rtcfile* directive defines the name of the file in which *chronyd* can
@@ -1421,7 +1498,7 @@ cannot be used with the <<rtcfile,*rtcfile*>> directive.
+ +
On Linux, the RTC copy is performed by the kernel every 11 minutes. On Linux, the RTC copy is performed by the kernel every 11 minutes.
+ +
On Mac OS X, <<chronyd,*chronyd*>> will perform the RTC copy every 60 minutes On macOS, <<chronyd,*chronyd*>> will perform the RTC copy every 60 minutes
when the system clock is in a synchronised state. when the system clock is in a synchronised state.
+ +
On other systems this directive does nothing. On other systems this directive does nothing.
@@ -1440,8 +1517,8 @@ called _measurements.log_. An example line (which actually appears as a single
line in the file) from the log file is shown below. line in the file) from the log file is shown below.
+ +
---- ----
2015-10-13 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \ 2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
-4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 CB00717B 4B D K
---- ----
+ +
The columns are as follows (the quantities in square brackets are the values The columns are as follows (the quantities in square brackets are the values
@@ -1471,6 +1548,13 @@ from the example line above):
. The peer dispersion (_epsilon_ in RFC 5905). [1.577e-05] . The peer dispersion (_epsilon_ in RFC 5905). [1.577e-05]
. The root delay (_DELTA_ in RFC 5905). [1.615e-01] . The root delay (_DELTA_ in RFC 5905). [1.615e-01]
. The root dispersion (_EPSILON_ in RFC 5905). [7.446e-03] . 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]
. 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]
+ +
*statistics*::: *statistics*:::
This option logs information about the regression processing to a file called This option logs information about the regression processing to a file called
@@ -1478,8 +1562,8 @@ _statistics.log_. An example line (which actually appears as a single line in
the file) from the log file is shown below. the file) from the log file is shown below.
+ +
---- ----
2015-07-22 05:40:50 203.0.113.15 6.261e-03 -3.247e-03 \ 2016-08-10 05:40:50 203.0.113.15 6.261e-03 -3.247e-03 \
2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8 2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8 0.00
---- ----
+ +
The columns are as follows (the quantities in square brackets are the values The columns are as follows (the quantities in square brackets are the values
@@ -1514,6 +1598,11 @@ from the example line above):
to be discarded. The number of runs for the data that is being retained is 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. tabulated. Values of approximately half the number of samples are expected.
[8] [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]
+ +
*tracking*::: *tracking*:::
This option logs changes to the estimate of the system's gain or loss rate, and This option logs changes to the estimate of the system's gain or loss rate, and
@@ -1693,6 +1782,43 @@ sendmail binary.
=== Miscellaneous === Miscellaneous
[[hwtimestamp]]*hwtimestamp* _interface_::
This directive enables hardware timestamping of NTP packets sent to and
received from the specified network interface. The network interface controller
(NIC) uses its own clock to accurately timestamp the actual transmissions and
receptions, avoiding processing and queueing delays in the kernel, network
driver, and hardware. This can significantly improve the accuracy of the
timestamps and the measured offset, which is used for synchronisation of the
system clock. In order to get the best results, both sides receiving and
sending NTP packets (i.e. server and client, or two peers) need to use HW
timestamping. If the server or peer supports the interleaved mode, it needs to
be enabled by the *xleave* option in the <<server,*server*>> or the
<<peer,*peer*>> directive.
+
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*.
+
If the specified interface is _*_, *chronyd* will try to enable HW timestamping
on all available interfaces.
+
An example of the directive is:
+
----
hwtimestamp eth0
----
[[include]]*include* _pattern_:: [[include]]*include* _pattern_::
The *include* directive includes a configuration file or multiple configuration The *include* directive includes a configuration file or multiple configuration
files if a wildcard pattern is specified. This can be useful when maintaining files if a wildcard pattern is specified. This can be useful when maintaining
@@ -1755,8 +1881,8 @@ significant impact on performance as *chronyd's* memory usage is modest. The
[[pidfile]]*pidfile* _file_:: [[pidfile]]*pidfile* _file_::
*chronyd* always writes its process ID (PID) to a file, and checks this file on *chronyd* always writes its process ID (PID) to a file, and checks this file on
startup to see if another *chronyd* may already be running on the system. By startup to see if another *chronyd* might already be running on the system. By
default, the file used is _/var/run/chronyd.pid_. The *pidfile* directive default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
allows the name to be changed, e.g.: allows the name to be changed, e.g.:
+ +
---- ----
@@ -1765,8 +1891,8 @@ pidfile /run/chronyd.pid
[[sched_priority]]*sched_priority* _priority_:: [[sched_priority]]*sched_priority* _priority_::
On Linux, the *sched_priority* directive will select the SCHED_FIFO real-time 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 Mac scheduler at the specified priority (which must be between 0 and 100). On
OS X, this option must have either a value of 0 (the default) to disable the 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 thread time constraint policy or 1 for the policy to be enabled. Other systems
do not support this option. do not support this option.
+ +
@@ -1781,7 +1907,7 @@ 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 unless you really need it. The *sched_setscheduler(2)* man page has more
details. details.
+ +
On Mac OS X, this directive uses the *thread_policy_set()* kernel call to 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 specify real-time scheduling. As noted for Linux, you should not use this
directive unless you really need it. directive unless you really need it.
@@ -1790,7 +1916,7 @@ The *user* directive sets the name of the system user to which *chronyd* will
switch after start in order to drop root privileges. switch after start in order to drop root privileges.
+ +
On Linux, *chronyd* needs to be compiled with support for the *libcap* library. On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On Mac OS X, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes. On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent. range of privileged system calls on behalf of the parent.
+ +
@@ -2094,6 +2220,34 @@ For the system shutdown, *chronyd* should receive a SIGTERM several seconds
before the final SIGKILL; the SIGTERM causes the measurement histories and RTC before the final SIGKILL; the SIGTERM causes the measurement histories and RTC
information to be saved. information to be saved.
=== Public NTP server
*chronyd* can be configured to operate as a public NTP server, e.g. to join the
http://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration
is similar to the NTP client with permanent connection, except it needs to
allow client access from all addresses. It is recommended to handpick at least
few good servers, and possibly combine them with a random selection of other
servers in the pool. The rate limiting interval can be increased to save more
bandwidth on misconfigured and broken NTP clients. The *-r* option with the
*dumpdir* directive shortens the time for which *chronyd* will not serve time
to its clients when it needs to be restarted for any reason.
The configuration file might be:
----
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
pool pool.ntp.org iburst
makestep 1.0 3
rtcsync
allow
ratelimit interval 1
driftfile @CHRONYVARDIR@/drift
dumpdir @CHRONYRUNDIR@
dumponexit
----
== SEE ALSO == SEE ALSO
<<chronyc.adoc#,*chronyc(1)*>>, <<chronyd.adoc#,*chronyd(8)*>> <<chronyc.adoc#,*chronyc(1)*>>, <<chronyd.adoc#,*chronyd(8)*>>

View File

@@ -44,7 +44,7 @@ There are two ways *chronyc* can access *chronyd*. One is the Internet
Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
accessible locally by the root or _chrony_ user. By default, *chronyc* first accessible locally by the root or _chrony_ user. By default, *chronyc* first
tries to connect to the Unix domain socket. The compiled-in default path is tries to connect to the Unix domain socket. The compiled-in default path is
_@CHRONYSOCKDIR@/chronyd.sock_. If that fails (e.g. because *chronyc* is _@CHRONYRUNDIR@/chronyd.sock_. If that fails (e.g. because *chronyc* is
running under a non-root user), it will try to connect to 127.0.0.1 and then running under a non-root user), it will try to connect to 127.0.0.1 and then
::1. ::1.
@@ -74,8 +74,8 @@ With this option hostnames will be resolved only to IPv4 addresses.
With this option hostnames will be resolved only to IPv6 addresses. With this option hostnames will be resolved only to IPv6 addresses.
*-n*:: *-n*::
This option disables resolving of IP addresses to hostnames (e.g. to avoid slow This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
DNS lookups). DNS lookups. Long addresses will not be truncated to fit into the column.
*-c*:: *-c*::
This option enables printing of reports in a comma-separated values (CSV) This option enables printing of reports in a comma-separated values (CSV)
@@ -127,7 +127,7 @@ The *tracking* command displays parameters about the system's clock
performance. An example of the output is shown below. performance. An example of the output is shown below.
+ +
---- ----
Reference ID : 203.0.113.15 (foo.example.net) Reference ID : CB00710F (foo.example.net)
Stratum : 3 Stratum : 3
Ref time (UTC) : Fri Feb 3 15:00:29 2012 Ref time (UTC) : Fri Feb 3 15:00:29 2012
System time : 0.000001501 seconds slow of NTP time System time : 0.000001501 seconds slow of NTP time
@@ -150,10 +150,14 @@ computer is currently synchronised. For IPv4 addresses, the reference ID is
equal to the address and for IPv6 addresses it is the first 32 bits of the MD5 equal to the address and for IPv6 addresses it is the first 32 bits of the MD5
sum of the address. sum of the address.
+ +
If it is _127.127.1.1_ it means the computer is not synchronised to any If the reference ID is _7F7F0101_ and there is no name or IP address, it means
external source and that you have the _local_ mode operating (via the the computer is not synchronised to any external source and that you have the
<<local,*local*>> command in *chronyc*, or the _local_ mode operating (via the <<local,*local*>> command in *chronyc*, or the
<<chrony.conf.adoc#local,*local*>> directive in the configuration file). <<chrony.conf.adoc#local,*local*>> directive in the configuration file).
+
The reference ID is printed as a hexadecimal number. Note that in older
versions it used to be printed in quad-dotted notation and could be confused
with an IPv4 address.
*Stratum*::: *Stratum*:::
The stratum indicates how many hops away from a computer with an attached The stratum indicates how many hops away from a computer with an attached
reference clock we are. Such a computer is a stratum-1 computer, so the reference clock we are. Such a computer is a stratum-1 computer, so the
@@ -437,6 +441,92 @@ the offline state.
the name of the server or peer was not resolved to an address yet; this source is the name of the server or peer was not resolved to an address yet; this source is
not visible in the *sources* and *sourcestats* reports. not visible in the *sources* and *sourcestats* reports.
[[ntpdata]]*ntpdata* [_address_]::
The *ntpdata* command displays the last valid measurement and other
NTP-specific information about the specified NTP source, or all NTP sources if
no address was specified. An example of the output is shown below.
+
----
Remote address : 203.0.113.15 (CB00710F)
Remote port : 123
Local address : 203.0.113.74 (CB00714A)
Leap status : Normal
Version : 4
Mode : Server
Stratum : 1
Poll interval : 10 (1024 seconds)
Precision : -24 (0.000000060 seconds)
Root delay : 0.000015 seconds
Root dispersion : 0.000015 seconds
Reference ID : 50505331
Reference time : Fri Nov 25 15:22:12 2016
Offset : -0.000060878 seconds
Peer delay : 0.000175634 seconds
Peer dispersion : 0.000000681 seconds
Response time : 0.000053050 seconds
Jitter asymmetry: +0.00
NTP tests : 111 111 1111
Interleaved : No
Authenticated : No
TX timestamping : Kernel
RX timestamping : Kernel
Total TX : 24
Total RX : 24
Total valid RX : 24
----
+
The fields are explained as follows:
+
*Remote address*:::
The IP address of the NTP server or peer, and the corresponding reference ID.
*Remote port*:::
The UDP port number to which the request was sent. The standard NTP port is
123.
*Local address*:::
The local IP address which received the response, and the corresponding
reference ID.
*Leap status*:::
*Version*:::
*Mode*:::
*Stratum*:::
*Poll interval*:::
*Precision*:::
*Root delay*:::
*Root dispersion*:::
*Reference ID*:::
*Reference time*:::
The NTP values from the last valid response.
*Offset*:::
*Peer delay*:::
*Peer dispersion*:::
The measured values.
*Response time*:::
The time the server or peer spent in processing of the request and waiting
before sending the response.
*Jitter asymmetry*:::
The estimated asymmetry of network jitter on the path to the source. 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.
*NTP tests*:::
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
delay, delay ratio, delay dev ratio, and synchronisation loop.
*Interleaved*:::
This shows if the response was in the interleaved mode.
*Authenticated*:::
This shows if the response was authenticated.
*TX timestamping*:::
The source of the local transmit timestamp. Valid values are _Daemon_,
_Kernel_, and _Hardware_.
*RX timestamping*:::
The source of the local receive timestamp.
*Total TX*:::
The number of packets sent to the source.
*Total RX*:::
The number of all packets received from the source.
*Total valid RX*:::
The number of valid packets received from the source.
[[add_peer]]*add peer* _address_ [_option_]...:: [[add_peer]]*add peer* _address_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst The *add peer* command allows a new NTP peer to be added whilst
*chronyd* is running. *chronyd* is running.

View File

@@ -75,14 +75,15 @@ This option is similar to *-q*, but it will only print the offset without any
corrections of the clock. corrections of the clock.
*-r*:: *-r*::
This option will reload sample histories for each of the servers and refclocks This option will try to reload and then delete files containing sample
being used. These histories are created by using the histories for each of the servers and reference clocks being used. These
<<chronyc.adoc#dump,*dump*>> command in *chronyc*, or by setting the histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
<<chrony.conf.adoc#dumponexit,*dumponexit*>> directive in the configuration *chronyc*, or by setting the <<chrony.conf.adoc#dumponexit,*dumponexit*>>
file. This option is useful if you want to stop and restart *chronyd* briefly directive in the configuration file. This option is useful if you want to stop
for any reason, e.g. to install a new version. However, it should be used only and restart *chronyd* briefly for any reason, e.g. to install a new version.
on systems where the kernel can maintain clock compensation whilst not under However, it should be used only on systems where the kernel can maintain clock
*chronyd*'s control (i.e. Linux, FreeBSD, NetBSD and Solaris). compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD
and Solaris).
*-R*:: *-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>> When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -109,13 +110,20 @@ time and the RTC time, the system time will be set to it to restore the time
when *chronyd* was previously stopped. This is useful on computers that have no when *chronyd* was previously stopped. This is useful on computers that have no
RTC or the RTC is broken (e.g. it has no battery). RTC or the RTC is broken (e.g. it has no battery).
*-t* _timeout_::
This option sets a timeout (in seconds) after which *chronyd* will exit. If the
clock is not synchronised, it will exit with a non-zero status. This is useful
with the *-q* or *-Q* option to shorten the maximum time waiting for
measurements, or with the *-r* option to limit the time when *chronyd* is
running, but still allow it to adjust the frequency of the system clock.
*-u* _user_:: *-u* _user_::
This option sets the name of the system user to which *chronyd* will switch This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive (default _@DEFAULT_USER@_). <<chrony.conf.adoc#user,*user*>> directive (default _@DEFAULT_USER@_).
+ +
On Linux, *chronyd* needs to be compiled with support for the *libcap* library. On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On Mac OS X, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes. On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent. range of privileged system calls on behalf of the parent.
@@ -134,7 +142,7 @@ killed even in normal operation.
*-P* _priority_:: *-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the On Linux, this option will select the SCHED_FIFO real-time scheduler at the
specified priority (which must be between 0 and 100). On Mac OS X, this option 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 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 constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. support this option.
@@ -161,4 +169,4 @@ https://chrony.tuxfamily.org/.
== AUTHORS == AUTHORS
chrony was written by Richard Curnow, Miroslav Lichvar and others. chrony was written by Richard Curnow, Miroslav Lichvar, and others.

View File

@@ -61,8 +61,8 @@ that.
In order to keep the real-time clock (RTC) close to the true time, so the 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 initialized on the
next boot from the RTC, the `rtcsync` directive enables a mode in which 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 Mac system time is periodically copied to the RTC. It is supported on Linux and
OS X. macOS.
If you want to use public NTP servers from the If you want to use public NTP servers from the
http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
@@ -148,14 +148,19 @@ network. It's better to use more than one server, three or four is usually
recommended as the minimum, so `chronyd` can detect servers that serve false recommended as the minimum, so `chronyd` can detect servers that serve false
time and combine measurements from multiple sources. time and combine measurements from multiple sources.
If you have a network card with hardware timestamping supported on Linux, it
can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It
should make local receive and transmit timestamps of NTP packets much more
accurate.
There are also useful options which can be set in the `server` directive, they There are also useful options which can be set in the `server` directive, they
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio` and are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`,
`maxdelaydevratio`. `maxdelaydevratio`, and `xleave`.
The first three options set the minimum and maximum allowed polling interval, 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 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 default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 6 (samples) for `polltarget`. The default values should be used `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 have
permission to poll some servers more frequently, setting these options for permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system shorter polling intervals may significantly improve the accuracy of the system
@@ -189,6 +194,16 @@ with local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2 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
----
server ntp.local minpoll 2 maxpoll 2 xleave
hwtimestamp eth0
----
=== What happened to the `commandkey` and `generatecommandkey` directives? === What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the They were removed in version 2.2. Authentication is no longer supported in the
@@ -287,12 +302,15 @@ authentication (`commandkey` directive).
=== Why does `chronyc tracking` always print an IPv4 address as reference ID? === Why does `chronyc tracking` always print an IPv4 address as reference ID?
The reference ID is a 32-bit value and is always printed in quad-dotted The reference ID is a 32-bit value and in versions before 3.0 it was printed in
notation, even if the reference source doesn't have an IPv4 address. For IPv4 quad-dotted notation, even if the reference source did not actually have an
addresses, the reference ID is equal to the address, but for IPv6 addresses it IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
is the first 32 bits of the MD5 sum of the address. For reference clocks, the for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
reference ID is the value specified with the `refid` option in the `refclock` reference clocks, the reference ID is the value specified with the `refid`
directive. option in the `refclock` directive.
Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
confusion with IPv4 addresses.
If you need to get the IP address of the current reference source, use the `-n` If you need to get the IP address of the current reference source, use the `-n`
option to disable resolving of IP addresses and read the second field (printed option to disable resolving of IP addresses and read the second field (printed
@@ -373,7 +391,8 @@ to serve time to clients in the network which support the broadcast client mode
=== Can `chronyd` keep the system clock a fixed offset away from real time? === Can `chronyd` keep the system clock a fixed offset away from real time?
This is not possible as the program currently stands. Yes. Starting from version 3.0, an offset can be specified by the `offset`
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? === What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?

View File

@@ -9,7 +9,7 @@ Wants=time-sync.target
Type=oneshot Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining # Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock correction to be less than 0.1 seconds # clock correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc waitsync 600 0.1 0.0 1 ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1
RemainAfterExit=yes RemainAfterExit=yes
StandardOutput=null StandardOutput=null

204
hwclock.c Normal file
View File

@@ -0,0 +1,204 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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.
*
**********************************************************************
=======================================================================
Tracking of hardware clocks (e.g. RTC, PHC)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "regress.h"
#include "util.h"
/* Maximum number of samples per clock */
#define MAX_SAMPLES 16
/* Minimum interval between samples (in seconds) */
#define MIN_SAMPLE_SEPARATION 1.0
struct HCL_Instance_Record {
/* HW and local reference timestamp */
struct timespec hw_ref;
struct timespec local_ref;
/* 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];
/* Number of samples */
int n_samples;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */
double offset;
double frequency;
};
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
HCL_Instance clock;
double delta;
clock = anything;
if (clock->n_samples)
UTI_AdjustTimespec(&clock->local_ref, cooked, &clock->local_ref, &delta, dfreq, doffset);
if (clock->valid_coefs)
clock->frequency /= 1.0 - dfreq;
}
/* ================================================== */
HCL_Instance
HCL_CreateInstance(void)
{
HCL_Instance clock;
clock = MallocNew(struct HCL_Instance_Record);
clock->x_data[MAX_SAMPLES - 1] = 0.0;
clock->y_data[MAX_SAMPLES - 1] = 0.0;
clock->n_samples = 0;
clock->valid_coefs = 0;
LCL_AddParameterChangeHandler(handle_slew, clock);
return clock;
}
/* ================================================== */
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
Free(clock);
}
/* ================================================== */
int
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
{
if (!clock->n_samples ||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= MIN_SAMPLE_SEPARATION)
return 1;
return 0;
}
/* ================================================== */
void
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err)
{
double hw_delta, local_delta, local_freq, raw_freq;
int i, n_runs, best_start;
local_freq = 1.0 - LCL_ReadAbsoluteFrequency() / 1.0e6;
/* Shift old samples */
if (clock->n_samples) {
if (clock->n_samples >= 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) {
clock->n_samples = 0;
DEBUG_LOG(LOGF_HwClocks, "HW clock reset interval=%f", local_delta);
}
for (i = MAX_SAMPLES - clock->n_samples; i < 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;
}
}
clock->n_samples++;
clock->hw_ref = *hw_ts;
clock->local_ref = *local_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,
&n_runs, &best_start);
if (!clock->valid_coefs) {
DEBUG_LOG(LOGF_HwClocks, "HW clock needs more samples");
return;
}
clock->frequency = raw_freq / local_freq;
/* Drop unneeded samples */
clock->n_samples -= best_start;
/* 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;
}
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,
UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
}
/* ================================================== */
int
HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked, double *err)
{
double offset, elapsed;
if (!clock->valid_coefs)
return 0;
elapsed = UTI_DiffTimespecsToDouble(raw, &clock->hw_ref);
offset = clock->offset + elapsed / clock->frequency;
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
/* Estimation of the error is not implemented yet */
if (err)
*err = 0.0;
return 1;
}

48
hwclock.h Normal file
View File

@@ -0,0 +1,48 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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 for tracking of hardware clocks */
#ifndef GOT_HWCLOCK_H
#define GOT_HWCLOCK_H
typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(void);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err);
/* Convert raw hardware time to cooked local time */
extern int HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
double *err);
#endif

103
keys.c
View File

@@ -103,9 +103,9 @@ static int
determine_hash_delay(uint32_t key_id) determine_hash_delay(uint32_t key_id)
{ {
NTP_Packet pkt; NTP_Packet pkt;
struct timeval before, after; struct timespec before, after;
unsigned long usecs, min_usecs=0; double diff, min_diff;
int i; int i, nsecs;
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before); LCL_ReadRawTime(&before);
@@ -113,19 +113,49 @@ determine_hash_delay(uint32_t key_id)
(unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data)); (unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
LCL_ReadRawTime(&after); LCL_ReadRawTime(&after);
usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec); diff = UTI_DiffTimespecsToDouble(&after, &before);
if (i == 0 || usecs < min_usecs) { if (i == 0 || min_diff > diff)
min_usecs = usecs; min_diff = diff;
}
} }
/* Add on a bit extra to allow for copying, conversions etc */ /* Add on a bit extra to allow for copying, conversions etc */
min_usecs += min_usecs >> 4; nsecs = 1.0625e9 * min_diff;
DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %ld useconds", key_id, min_usecs); DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
return min_usecs; return nsecs;
}
/* ================================================== */
/* Decode password encoded in ASCII or HEX */
static int
decode_password(char *key)
{
int i, j, len = strlen(key);
char buf[3], *p;
if (!strncmp(key, "ASCII:", 6)) {
memmove(key, key + 6, len - 6);
return len - 6;
} else if (!strncmp(key, "HEX:", 4)) {
if ((len - 4) % 2)
return 0;
for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
key[i] = strtol(buf, &p, 16);
if (p != buf + 2)
return 0;
}
return i;
} else {
/* assume ASCII */
return len;
}
} }
/* ================================================== */ /* ================================================== */
@@ -192,7 +222,7 @@ KEY_Reload(void)
continue; continue;
} }
key.len = UTI_DecodePasswordFromText(keyval); key.len = decode_password(keyval);
if (!key.len) { if (!key.len) {
LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id); LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id);
continue; continue;
@@ -292,6 +322,22 @@ KEY_GetAuthDelay(uint32_t key_id)
/* ================================================== */ /* ================================================== */
int
KEY_GetAuthLength(uint32_t key_id)
{
unsigned char buf[MAX_HASH_LENGTH];
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
}
/* ================================================== */
int int
KEY_CheckKeyLength(uint32_t key_id) KEY_CheckKeyLength(uint32_t key_id)
{ {
@@ -307,6 +353,31 @@ KEY_CheckKeyLength(uint32_t key_id)
/* ================================================== */ /* ================================================== */
static int
generate_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
{
return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
}
/* ================================================== */
static int
check_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
}
/* ================================================== */
int int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len, KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len) unsigned char *auth, int auth_len)
@@ -318,15 +389,15 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key) if (!key)
return 0; return 0;
return UTI_GenerateNTPAuth(key->hash_id, (unsigned char *)key->val, return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
key->len, data, data_len, auth, auth_len); data, data_len, auth, auth_len);
} }
/* ================================================== */ /* ================================================== */
int int
KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len) const unsigned char *auth, int auth_len, int trunc_len)
{ {
Key *key; Key *key;
@@ -335,6 +406,6 @@ KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key) if (!key)
return 0; return 0;
return UTI_CheckNTPAuth(key->hash_id, (unsigned char *)key->val, return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
key->len, data, data_len, auth, auth_len); data, data_len, auth, auth_len, trunc_len);
} }

5
keys.h
View File

@@ -37,11 +37,12 @@ extern void KEY_Reload(void);
extern int KEY_GetKey(uint32_t key_id, char **key, int *len); extern int KEY_GetKey(uint32_t key_id, char **key, int *len);
extern int KEY_KeyKnown(uint32_t key_id); extern int KEY_KeyKnown(uint32_t key_id);
extern int KEY_GetAuthDelay(uint32_t key_id); extern int KEY_GetAuthDelay(uint32_t key_id);
extern int KEY_GetAuthLength(uint32_t key_id);
extern int KEY_CheckKeyLength(uint32_t key_id); extern int KEY_CheckKeyLength(uint32_t key_id);
extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data,
int data_len, unsigned char *auth, int auth_len); int data_len, unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
int data_len, const unsigned char *auth, int auth_len); const unsigned char *auth, int auth_len, int trunc_len);
#endif /* GOT_KEYS_H */ #endif /* GOT_KEYS_H */

85
local.c
View File

@@ -106,37 +106,42 @@ static double max_clock_error;
under 1s of busy waiting. */ under 1s of busy waiting. */
#define NITERS 100 #define NITERS 100
#define NSEC_PER_SEC 1000000000
static void static void
calculate_sys_precision(void) calculate_sys_precision(void)
{ {
struct timeval tv, old_tv; struct timespec ts, old_ts;
int dusec, best_dusec; int iters, diff, best;
int iters;
gettimeofday(&old_tv, NULL); LCL_ReadRawTime(&old_ts);
best_dusec = 1000000; /* Assume we must be better than a second */
/* Assume we must be better than a second */
best = NSEC_PER_SEC;
iters = 0; iters = 0;
do { do {
gettimeofday(&tv, NULL); LCL_ReadRawTime(&ts);
dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
old_tv = tv; diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
if (dusec > 0) {
if (dusec < best_dusec) { old_ts = ts;
best_dusec = dusec; if (diff > 0) {
} if (diff < best)
best = diff;
iters++; iters++;
} }
} while (iters < NITERS); } while (iters < NITERS);
assert(best_dusec > 0); assert(best > 0);
precision_quantum = best_dusec * 1.0e-6; precision_quantum = 1.0e-9 * best;
/* Get rounded log2 value of the measured precision */ /* Get rounded log2 value of the measured precision */
precision_log = 0; precision_log = 0;
while (best_dusec < 707107) { while (best < 707106781) {
precision_log--; precision_log--;
best_dusec *= 2; best *= 2;
} }
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log); DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
@@ -278,7 +283,7 @@ LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
/* ================================================== */ /* ================================================== */
static void static void
invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked, invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
double dfreq, double doffset, double dfreq, double doffset,
LCL_ChangeType change_type) LCL_ChangeType change_type)
{ {
@@ -345,23 +350,29 @@ void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void
} }
/* ================================================== */ /* ================================================== */
/* At the moment, this is just gettimeofday(), because
I can't think of a Unix system where it would not be */
void void
LCL_ReadRawTime(struct timeval *result) LCL_ReadRawTime(struct timespec *ts)
{ {
if (gettimeofday(result, NULL) < 0) { #if HAVE_CLOCK_GETTIME
LOG_FATAL(LOGF_Local, "gettimeofday() failed"); if (clock_gettime(CLOCK_REALTIME, ts) < 0)
} LOG_FATAL(LOGF_Local, "clock_gettime() failed : %s", strerror(errno));
#else
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
LOG_FATAL(LOGF_Local, "gettimeofday() failed : %s", strerror(errno));
UTI_TimevalToTimespec(&tv, ts);
#endif
} }
/* ================================================== */ /* ================================================== */
void void
LCL_ReadCookedTime(struct timeval *result, double *err) LCL_ReadCookedTime(struct timespec *result, double *err)
{ {
struct timeval raw; struct timespec raw;
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, result, err); LCL_CookTime(&raw, result, err);
@@ -370,18 +381,18 @@ LCL_ReadCookedTime(struct timeval *result, double *err)
/* ================================================== */ /* ================================================== */
void void
LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err) LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
{ {
double correction; double correction;
LCL_GetOffsetCorrection(raw, &correction, err); LCL_GetOffsetCorrection(raw, &correction, err);
UTI_AddDoubleToTimeval(raw, correction, cooked); UTI_AddDoubleToTimespec(raw, correction, cooked);
} }
/* ================================================== */ /* ================================================== */
void void
LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err) LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
{ {
/* Call system specific driver to get correction */ /* Call system specific driver to get correction */
(*drv_offset_convert)(raw, correction, err); (*drv_offset_convert)(raw, correction, err);
@@ -421,7 +432,7 @@ clamp_freq(double freq)
/* ================================================== */ /* ================================================== */
static int static int
check_offset(struct timeval *now, double offset) check_offset(struct timespec *now, double offset)
{ {
/* Check if the time will be still sane with accumulated offset */ /* Check if the time will be still sane with accumulated offset */
if (UTI_IsTimeOffsetSane(now, -offset)) if (UTI_IsTimeOffsetSane(now, -offset))
@@ -439,7 +450,7 @@ check_offset(struct timeval *now, double offset)
void void
LCL_SetAbsoluteFrequency(double afreq_ppm) LCL_SetAbsoluteFrequency(double afreq_ppm)
{ {
struct timeval raw, cooked; struct timespec raw, cooked;
double dfreq; double dfreq;
afreq_ppm = clamp_freq(afreq_ppm); afreq_ppm = clamp_freq(afreq_ppm);
@@ -470,7 +481,7 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
void void
LCL_AccumulateDeltaFrequency(double dfreq) LCL_AccumulateDeltaFrequency(double dfreq)
{ {
struct timeval raw, cooked; struct timespec raw, cooked;
double old_freq_ppm; double old_freq_ppm;
old_freq_ppm = current_freq_ppm; old_freq_ppm = current_freq_ppm;
@@ -499,7 +510,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
void void
LCL_AccumulateOffset(double offset, double corr_rate) LCL_AccumulateOffset(double offset, double corr_rate)
{ {
struct timeval raw, cooked; struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients /* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */ has to be the cooked time BEFORE the change was made */
@@ -521,7 +532,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
int int
LCL_ApplyStepOffset(double offset) LCL_ApplyStepOffset(double offset)
{ {
struct timeval raw, cooked; struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients /* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */ has to be the cooked time BEFORE the change was made */
@@ -549,7 +560,7 @@ LCL_ApplyStepOffset(double offset)
/* ================================================== */ /* ================================================== */
void void
LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked, LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion) double offset, double dispersion)
{ {
/* Dispatch to all handlers */ /* Dispatch to all handlers */
@@ -563,7 +574,7 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
void void
LCL_NotifyLeap(int leap) LCL_NotifyLeap(int leap)
{ {
struct timeval raw, cooked; struct timespec raw, cooked;
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
@@ -580,7 +591,7 @@ LCL_NotifyLeap(int leap)
void void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{ {
struct timeval raw, cooked; struct timespec raw, cooked;
double old_freq_ppm; double old_freq_ppm;
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
@@ -657,7 +668,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
int int
LCL_MakeStep(void) LCL_MakeStep(void)
{ {
struct timeval raw; struct timespec raw;
double correction; double correction;
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);

15
local.h
View File

@@ -31,9 +31,8 @@
#include "sysincl.h" #include "sysincl.h"
/* Read the system clock. This is analogous to gettimeofday(), /* Read the system clock */
but with the timezone information ignored */ extern void LCL_ReadRawTime(struct timespec *ts);
extern void LCL_ReadRawTime(struct timeval *);
/* Read the system clock, corrected according to all accumulated /* Read the system clock, corrected according to all accumulated
drifts and uncompensated offsets. drifts and uncompensated offsets.
@@ -44,15 +43,15 @@ extern void LCL_ReadRawTime(struct timeval *);
adjtime()-like interface to correct offsets, and to adjust the adjtime()-like interface to correct offsets, and to adjust the
frequency), we must correct the raw time to get this value */ frequency), we must correct the raw time to get this value */
extern void LCL_ReadCookedTime(struct timeval *t, double *err); extern void LCL_ReadCookedTime(struct timespec *ts, double *err);
/* Convert raw time to cooked. */ /* Convert raw time to cooked. */
extern void LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err); extern void LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err);
/* Read the current offset between the system clock and true time /* Read the current offset between the system clock and true time
(i.e. 'cooked' - 'raw') (in seconds). */ (i.e. 'cooked' - 'raw') (in seconds). */
extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err); extern void LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err);
/* Type of routines that may be invoked as callbacks when there is a /* Type of routines that may be invoked as callbacks when there is a
change to the frequency or offset. change to the frequency or offset.
@@ -79,7 +78,7 @@ typedef enum {
} LCL_ChangeType; } LCL_ChangeType;
typedef void (*LCL_ParameterChangeHandler) typedef void (*LCL_ParameterChangeHandler)
(struct timeval *raw, struct timeval *cooked, (struct timespec *raw, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -163,7 +162,7 @@ extern int LCL_ApplyStepOffset(double offset);
/* Routine to invoke notify handlers on an unexpected time jump /* Routine to invoke notify handlers on an unexpected time jump
in system clock */ in system clock */
extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked, extern void LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion); double offset, double dispersion);
/* Routine to invoke notify handlers on leap second when the system clock /* Routine to invoke notify handlers on leap second when the system clock

View File

@@ -52,7 +52,7 @@ typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
/* System driver to convert a raw time to an adjusted (cooked) time. /* System driver to convert a raw time to an adjusted (cooked) time.
The number of seconds returned in 'corr' have to be added to the The number of seconds returned in 'corr' have to be added to the
raw time to get the corrected time */ raw time to get the corrected time */
typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr, double *err); typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err);
/* System driver to schedule leap second */ /* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap); typedef void (*lcl_SetLeapDriver)(int leap);

View File

@@ -238,12 +238,18 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return; return;
if (!logfiles[id].file) { if (!logfiles[id].file) {
char filename[512]; char filename[512], *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') {
LOG(LOGS_WARN, LOGF_Logging, "logdir not specified");
logfiles[id].name = NULL;
return;
}
if (snprintf(filename, sizeof(filename), "%s/%s.log", if (snprintf(filename, sizeof(filename), "%s/%s.log",
CNF_GetLogDir(), logfiles[id].name) >= sizeof(filename) || logdir, logfiles[id].name) >= sizeof (filename) ||
!(logfiles[id].file = fopen(filename, "a"))) { !(logfiles[id].file = fopen(filename, "a"))) {
LOG(LOGS_WARN, LOGF_Refclock, "Couldn't open logfile %s for update", filename); LOG(LOGS_WARN, LOGF_Logging, "Could not open log file %s", filename);
logfiles[id].name = NULL; logfiles[id].name = NULL;
return; return;
} }

View File

@@ -47,10 +47,10 @@ extern int log_debug_enabled;
#if DEBUG > 0 #if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \ #define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#else #else
#define LOG_MESSAGE(severity, facility, ...) \ #define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, __VA_ARGS__); LOG_Message(severity, __VA_ARGS__)
#endif #endif
#define DEBUG_LOG(facility, ...) \ #define DEBUG_LOG(facility, ...) \
@@ -82,7 +82,9 @@ typedef enum {
typedef enum { typedef enum {
LOGF_Reference, LOGF_Reference,
LOGF_NtpIO, LOGF_NtpIO,
LOGF_NtpIOLinux,
LOGF_NtpCore, LOGF_NtpCore,
LOGF_NtpSignd,
LOGF_NtpSources, LOGF_NtpSources,
LOGF_Scheduler, LOGF_Scheduler,
LOGF_SourceStats, LOGF_SourceStats,
@@ -114,6 +116,7 @@ typedef enum {
LOGF_TempComp, LOGF_TempComp,
LOGF_RtcLinux, LOGF_RtcLinux,
LOGF_Refclock, LOGF_Refclock,
LOGF_HwClocks,
LOGF_Smooth, LOGF_Smooth,
} LOG_Facility; } LOG_Facility;

33
main.c
View File

@@ -35,6 +35,7 @@
#include "local.h" #include "local.h"
#include "sys.h" #include "sys.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_signd.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "sources.h" #include "sources.h"
@@ -107,6 +108,7 @@ MAI_CleanupAndExit(void)
TMC_Finalise(); TMC_Finalise();
MNL_Finalise(); MNL_Finalise();
CLG_Finalise(); CLG_Finalise();
NSD_Finalise();
NSR_Finalise(); NSR_Finalise();
SST_Finalise(); SST_Finalise();
NCR_Finalise(); NCR_Finalise();
@@ -143,6 +145,16 @@ signal_cleanup(int x)
/* ================================================== */ /* ================================================== */
static void
quit_timeout(void *arg)
{
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();
}
/* ================================================== */
static void static void
ntp_source_resolving_end(void) ntp_source_resolving_end(void)
{ {
@@ -156,6 +168,7 @@ ntp_source_resolving_end(void)
SRC_ReloadSources(); SRC_ReloadSources();
} }
SRC_RemoveDumpFiles();
RTC_StartMeasurements(); RTC_StartMeasurements();
RCL_StartRefclocks(); RCL_StartRefclocks();
NSR_StartSources(); NSR_StartSources();
@@ -294,14 +307,14 @@ go_daemon(void)
/* Create pipe which will the daemon use to notify the grandparent /* Create pipe which will the daemon use to notify the grandparent
when it's initialised or send an error message */ when it's initialised or send an error message */
if (pipe(pipefd)) { if (pipe(pipefd)) {
LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno)); LOG_FATAL(LOGF_Main, "Could not detach, pipe failed : %s", strerror(errno));
} }
/* Does this preserve existing signal handlers? */ /* Does this preserve existing signal handlers? */
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) { } else if (pid > 0) {
/* In the 'grandparent' */ /* In the 'grandparent' */
char message[1024]; char message[1024];
@@ -326,7 +339,7 @@ go_daemon(void)
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) { } else if (pid > 0) {
exit(0); /* In the 'parent' */ exit(0); /* In the 'parent' */
} else { } else {
@@ -334,7 +347,7 @@ go_daemon(void)
/* Change current directory to / */ /* Change current directory to / */
if (chdir("/") < 0) { if (chdir("/") < 0) {
LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno)); LOG_FATAL(LOGF_Main, "Could not chdir to / : %s", strerror(errno));
} }
/* Don't keep stdin/out/err from before. But don't close /* Don't keep stdin/out/err from before. But don't close
@@ -359,7 +372,7 @@ int main
char *user = NULL; char *user = NULL;
struct passwd *pw; struct passwd *pw;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0; int do_init_rtc = 0, restarted = 0, timeout = 0;
int other_pid; int other_pid;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0; int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int system_log = 1; int system_log = 1;
@@ -417,12 +430,16 @@ int main
ref_mode = REF_ModePrintOnce; ref_mode = REF_ModePrintOnce;
nofork = 1; nofork = 1;
system_log = 0; 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)) { } else if (!strcmp("-4", *argv)) {
address_family = IPADDR_INET4; address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) { } else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6; address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) { } else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n", printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname); progname);
return 0; return 0;
} else if (*argv[0] == '-') { } else if (*argv[0] == '-') {
@@ -523,6 +540,7 @@ int main
REF_Initialise(); REF_Initialise();
SST_Initialise(); SST_Initialise();
NSR_Initialise(); NSR_Initialise();
NSD_Initialise();
CLG_Initialise(); CLG_Initialise();
MNL_Initialise(); MNL_Initialise();
TMC_Initialise(); TMC_Initialise();
@@ -545,6 +563,9 @@ int main
REF_SetModeEndHandler(reference_mode_end); REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode); REF_SetMode(ref_mode);
if (timeout)
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
if (do_init_rtc) { if (do_init_rtc) {
RTC_TimeInit(post_init_rtc_hook, NULL); RTC_TimeInit(post_init_rtc_hook, NULL);
} else { } else {

View File

@@ -47,7 +47,7 @@ static int enabled = 0;
/* More recent samples at highest indices */ /* More recent samples at highest indices */
typedef struct { typedef struct {
struct timeval when; /* This is our 'cooked' time */ struct timespec when; /* This is our 'cooked' time */
double orig_offset; /*+ Not modified by slew samples */ double orig_offset; /*+ Not modified by slew samples */
double offset; /*+ if we are fast of the supplied reference */ double offset; /*+ if we are fast of the supplied reference */
double residual; /*+ regression residual (sign convention given by double residual; /*+ regression residual (sign convention given by
@@ -64,8 +64,8 @@ static int n_samples;
/* ================================================== */ /* ================================================== */
static void static void
slew_samples(struct timeval *raw, slew_samples(struct timespec *raw,
struct timeval *cooked, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -97,7 +97,7 @@ MNL_Finalise(void)
/* ================================================== */ /* ================================================== */
static void static void
estimate_and_set_system(struct timeval *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, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
{ {
double agos[MAX_SAMPLES], offsets[MAX_SAMPLES]; double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
double b0, b1; double b0, b1;
@@ -110,7 +110,7 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
if (n_samples > 1) { if (n_samples > 1) {
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
UTI_DiffTimevalsToDouble(&agos[i], &samples[n_samples-1].when, &samples[i].when); agos[i] = UTI_DiffTimespecsToDouble(&samples[n_samples - 1].when, &samples[i].when);
offsets[i] = samples[i].offset; offsets[i] = samples[i].offset;
} }
@@ -173,9 +173,9 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
/* ================================================== */ /* ================================================== */
int int
MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm) MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
{ {
struct timeval now; struct timespec now;
double offset, diff; double offset, diff;
int i; int i;
@@ -189,12 +189,12 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
return 0; return 0;
if (n_samples) { if (n_samples) {
UTI_DiffTimevalsToDouble(&diff, &now, &samples[n_samples - 1].when); diff = UTI_DiffTimespecsToDouble(&now, &samples[n_samples - 1].when);
if (diff < MIN_SAMPLE_SEPARATION) if (diff < MIN_SAMPLE_SEPARATION)
return 0; return 0;
} }
UTI_DiffTimevalsToDouble(&offset, &now, ts); offset = UTI_DiffTimespecsToDouble(&now, ts);
/* Check if buffer full up */ /* Check if buffer full up */
if (n_samples == MAX_SAMPLES) { if (n_samples == MAX_SAMPLES) {
@@ -224,8 +224,8 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
/* ================================================== */ /* ================================================== */
static void static void
slew_samples(struct timeval *raw, slew_samples(struct timespec *raw,
struct timeval *cooked, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -239,7 +239,7 @@ slew_samples(struct timeval *raw,
} }
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
UTI_AdjustTimeval(&samples[i].when, cooked, &samples[i].when, &delta_time, UTI_AdjustTimespec(&samples[i].when, cooked, &samples[i].when, &delta_time,
dfreq, doffset); dfreq, doffset);
samples[i].offset += delta_time; samples[i].offset += delta_time;
} }
@@ -309,7 +309,7 @@ int
MNL_DeleteSample(int index) MNL_DeleteSample(int index)
{ {
int i; int i;
struct timeval now; struct timespec now;
if ((index < 0) || (index >= n_samples)) { if ((index < 0) || (index >= n_samples)) {
return 0; return 0;

View File

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

View File

@@ -72,7 +72,7 @@ start_resolving(void *anything)
/* ================================================== */ /* ================================================== */
static void static void
end_resolving(void *anything) end_resolving(int fd, int event, void *anything)
{ {
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything; struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
int i; int i;
@@ -83,7 +83,7 @@ end_resolving(void *anything)
resolving_threads--; resolving_threads--;
SCH_RemoveInputFileHandler(inst->pipe[0]); SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]); close(inst->pipe[0]);
close(inst->pipe[1]); close(inst->pipe[1]);
@@ -113,6 +113,9 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
LOG_FATAL(LOGF_Nameserv, "pipe() failed"); LOG_FATAL(LOGF_Nameserv, "pipe() failed");
} }
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
resolving_threads++; resolving_threads++;
assert(resolving_threads <= 1); assert(resolving_threads <= 1);
@@ -120,7 +123,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed"); LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
} }
SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst); SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst);
} }
/* ================================================== */ /* ================================================== */

4
ntp.h
View File

@@ -56,6 +56,10 @@ typedef uint32_t NTP_int32;
#define NTP_MIN_MAC_LENGTH (4 + 16) #define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH) #define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
/* The maximum length of MAC in NTPv4 packets which allows deterministic
parsing of extension fields (RFC 7822) */
#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
/* Type definition for leap bits */ /* Type definition for leap bits */
typedef enum { typedef enum {
LEAP_Normal = 0, LEAP_Normal = 0,

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,18 @@ typedef enum {
NTP_SERVER, NTP_PEER NTP_SERVER, NTP_PEER
} NTP_Source_Type; } NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct {
struct timespec ts;
double err;
NTP_Timestamp_Source source;
} NTP_Local_Timestamp;
/* This is a private data type used for storing the instance record for /* This is a private data type used for storing the instance record for
each source that we are chiming with */ each source that we are chiming with */
typedef struct NCR_Instance_Record *NCR_Instance; typedef struct NCR_Instance_Record *NCR_Instance;
@@ -58,19 +70,34 @@ extern void NCR_StartInstance(NCR_Instance instance);
/* Reset an instance */ /* Reset an instance */
extern void NCR_ResetInstance(NCR_Instance inst); extern void NCR_ResetInstance(NCR_Instance inst);
/* Reset polling interval of an instance */
extern void NCR_ResetPoll(NCR_Instance instance);
/* Change the remote address of an instance */ /* Change the remote address of an instance */
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr); extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
/* This routine is called when a new packet arrives off the network, /* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */ and it relates to a source we have an ongoing protocol exchange with */
extern int NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, NTP_Local_Address *local_addr, int length); extern int NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a new packet arrives off the network, /* This routine is called when a new packet arrives off the network,
and we do not recognize its source */ and we do not recognize its source */
extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); extern void NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a source we have
an ongoing protocol exchange with */
extern void NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a destination we
do not recognize */
extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Slew receive and transmit times in instance records */ /* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset); extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
/* Take a particular source online (i.e. start sampling it) */ /* Take a particular source online (i.e. start sampling it) */
extern void NCR_TakeSourceOnline(NCR_Instance inst); extern void NCR_TakeSourceOnline(NCR_Instance inst);
@@ -95,7 +122,8 @@ extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples); extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now); extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all); extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
extern int NCR_CheckAccessRestriction(IPAddr *ip_addr); extern int NCR_CheckAccessRestriction(IPAddr *ip_addr);

336
ntp_io.c
View File

@@ -30,6 +30,7 @@
#include "sysincl.h" #include "sysincl.h"
#include "array.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_sources.h" #include "ntp_sources.h"
@@ -40,7 +41,12 @@
#include "privops.h" #include "privops.h"
#include "util.h" #include "util.h"
#ifdef HAVE_LINUX_TIMESTAMPING
#include "ntp_io_linux.h"
#endif
#define INVALID_SOCK_FD -1 #define INVALID_SOCK_FD -1
#define CMSGBUF_SIZE 256
union sockaddr_in46 { union sockaddr_in46 {
struct sockaddr_in in4; struct sockaddr_in in4;
@@ -50,6 +56,31 @@ union sockaddr_in46 {
struct sockaddr u; struct sockaddr u;
}; };
struct Message {
union sockaddr_in46 name;
struct iovec iov;
NTP_Receive_Buffer buf;
/* Aligned buffer for control messages */
struct cmsghdr cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
};
#ifdef HAVE_RECVMMSG
#define MAX_RECV_MESSAGES 4
#define MessageHeader mmsghdr
#else
/* Compatible with mmsghdr */
struct MessageHeader {
struct msghdr msg_hdr;
unsigned int msg_len;
};
#define MAX_RECV_MESSAGES 1
#endif
/* Arrays of Message and MessageHeader */
static ARR_Instance recv_messages;
static ARR_Instance recv_headers;
/* The server/peer and client sockets for IPv4 and IPv6 */ /* The server/peer and client sockets for IPv4 and IPv6 */
static int server_sock_fd4; static int server_sock_fd4;
static int client_sock_fd4; static int client_sock_fd4;
@@ -80,7 +111,7 @@ static int initialised=0;
/* ================================================== */ /* ================================================== */
/* Forward prototypes */ /* Forward prototypes */
static void read_from_socket(void *anything); static void read_from_socket(int sock_fd, int event, void *anything);
/* ================================================== */ /* ================================================== */
@@ -91,7 +122,7 @@ prepare_socket(int family, int port_number, int client_only)
socklen_t my_addr_len; socklen_t my_addr_len;
int sock_fd; int sock_fd;
IPAddr bind_address; IPAddr bind_address;
int on_off = 1; int events = SCH_FILE_INPUT, on_off = 1;
/* Open Internet domain UDP socket for NTP message transmissions */ /* Open Internet domain UDP socket for NTP message transmissions */
@@ -175,12 +206,20 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef SO_TIMESTAMP #ifdef SO_TIMESTAMP
/* Enable receiving of timestamp control messages */ /* 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) { 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"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP");
/* Don't quit - we might survive anyway */ /* Don't quit - we might survive anyway */
} }
#endif #endif
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events);
#endif
#ifdef IP_FREEBIND #ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */ /* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 && if (my_addr_len > 0 &&
@@ -229,8 +268,8 @@ prepare_socket(int family, int port_number, int client_only)
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
/* Register handler for read events on the socket */ /* Register handler for read and possibly exception events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd); SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
return sock_fd; return sock_fd;
} }
@@ -282,11 +321,38 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD) if (sock_fd == INVALID_SOCK_FD)
return; return;
SCH_RemoveInputFileHandler(sock_fd); SCH_RemoveFileHandler(sock_fd);
close(sock_fd); close(sock_fd);
} }
/* ================================================== */ /* ================================================== */
static void
prepare_buffers(unsigned int n)
{
struct MessageHeader *hdr;
struct Message *msg;
unsigned int i;
for (i = 0; i < n; i++) {
msg = ARR_GetElement(recv_messages, i);
hdr = ARR_GetElement(recv_headers, i);
msg->iov.iov_base = &msg->buf;
msg->iov.iov_len = sizeof (msg->buf);
hdr->msg_hdr.msg_name = &msg->name;
hdr->msg_hdr.msg_namelen = sizeof (msg->name);
hdr->msg_hdr.msg_iov = &msg->iov;
hdr->msg_hdr.msg_iovlen = 1;
hdr->msg_hdr.msg_control = &msg->cmsgbuf;
hdr->msg_hdr.msg_controllen = sizeof (msg->cmsgbuf);
hdr->msg_hdr.msg_flags = 0;
hdr->msg_len = 0;
}
}
/* ================================================== */
void void
NIO_Initialise(int family) NIO_Initialise(int family)
{ {
@@ -295,6 +361,19 @@ NIO_Initialise(int family)
assert(!initialised); assert(!initialised);
initialised = 1; initialised = 1;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise();
#else
if (ARR_GetSize(CNF_GetHwTsInterfaces()))
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
#endif
recv_messages = ARR_CreateInstance(sizeof (struct Message));
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
prepare_buffers(MAX_RECV_MESSAGES);
server_port = CNF_GetNTPPort(); server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort(); client_port = CNF_GetAcquisitionPort();
@@ -367,6 +446,13 @@ NIO_Finalise(void)
close_socket(server_sock_fd6); close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#endif #endif
ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages);
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise();
#endif
initialised = 0; initialised = 0;
} }
@@ -482,116 +568,174 @@ NIO_IsServerSocket(int sock_fd)
/* ================================================== */ /* ================================================== */
static void static void
read_from_socket(void *anything) process_message(struct msghdr *hdr, int length, int sock_fd)
{ {
/* This should only be called when there is something
to read, otherwise it will block. */
int status, sock_fd;
NTP_Receive_Buffer message;
union sockaddr_in46 where_from;
unsigned int flags = 0;
struct timeval now;
double now_err;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
char cmsgbuf[256]; NTP_Local_Timestamp local_ts;
struct msghdr msg; struct timespec sched_ts;
struct iovec iov;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
int if_index;
assert(initialised); SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON;
sched_ts = local_ts.ts;
SCH_GetLastEventTime(&now, &now_err, NULL); if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
DEBUG_LOG(LOGF_NtpIO, "Truncated source address");
return;
}
iov.iov_base = &message.ntp_pkt; if (hdr->msg_namelen >= sizeof (((struct sockaddr *)hdr->msg_name)->sa_family)) {
iov.iov_len = sizeof(message); UTI_SockaddrToIPAndPort((struct sockaddr *)hdr->msg_name,
msg.msg_name = &where_from; &remote_addr.ip_addr, &remote_addr.port);
msg.msg_namelen = sizeof(where_from); } else {
msg.msg_iov = &iov; remote_addr.ip_addr.family = IPADDR_UNSPEC;
msg.msg_iovlen = 1; remote_addr.port = 0;
msg.msg_control = (void *) cmsgbuf; }
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = 0;
sock_fd = (long)anything; local_addr.ip_addr.family = IPADDR_UNSPEC;
status = recvmsg(sock_fd, &msg, flags); local_addr.sock_fd = sock_fd;
if_index = -1;
/* Don't bother checking if read failed or why if it did. More if (hdr->msg_flags & MSG_TRUNC) {
likely than not, it will be connection refused, resulting from a DEBUG_LOG(LOGF_NtpIO, "Received truncated message from %s:%d",
previous sendto() directing a datagram at a port that is not UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
listening (which appears to generate an ICMP response, and on return;
some architectures e.g. Linux this is translated into an error }
reponse on a subsequent recvfrom). */
if (status > 0) { if (hdr->msg_flags & MSG_CTRUNC) {
if (msg.msg_namelen > sizeof (where_from)) DEBUG_LOG(LOGF_NtpIO, "Truncated control message");
LOG_FATAL(LOGF_NtpIO, "Truncated source address"); /* Continue */
}
UTI_SockaddrToIPAndPort(&where_from.u, &remote_addr.ip_addr, &remote_addr.port); for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = sock_fd;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
#ifdef HAVE_IN_PKTINFO #ifdef HAVE_IN_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi; struct in_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr); local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4; local_addr.ip_addr.family = IPADDR_INET4;
} if_index = ipi.ipi_ifindex;
}
#endif #endif
#ifdef HAVE_IN6_PKTINFO #ifdef HAVE_IN6_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi; struct in6_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr, memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6)); sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6; local_addr.ip_addr.family = IPADDR_INET6;
} if_index = ipi.ipi6_ifindex;
}
#endif #endif
#ifdef SO_TIMESTAMP #ifdef SCM_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval tv; struct timeval tv;
struct timespec ts;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv)); memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
LCL_CookTime(&tv, &now, &now_err); UTI_TimevalToTimespec(&tv, &ts);
} LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
}
#endif #endif
#ifdef SCM_TIMESTAMPNS
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
struct timespec ts;
memcpy(&ts, CMSG_DATA(cmsg), sizeof (ts));
LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
} }
#endif
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
status, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
if (status >= NTP_NORMAL_PACKET_LENGTH) {
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
&remote_addr, &local_addr, status);
} else {
/* Just ignore the packet if it's not of a recognized length */
}
} }
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts,
hdr, length, sock_fd, if_index))
return;
#endif
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, if_index,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
/* Just ignore the packet if it's not of a recognized length */
if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer))
return;
NSR_ProcessRx(&remote_addr, &local_addr, &local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
}
/* ================================================== */
static void
read_from_socket(int sock_fd, int event, void *anything)
{
/* This should only be called when there is something
to read, otherwise it may block */
struct MessageHeader *hdr;
unsigned int i, n;
int status, flags = 0;
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
assert(n >= 1);
if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING
flags |= MSG_ERRQUEUE;
#else
assert(0);
#endif
}
#ifdef HAVE_RECVMMSG
status = recvmmsg(sock_fd, hdr, n, flags | MSG_DONTWAIT, NULL);
if (status >= 0)
n = status;
#else
n = 1;
status = recvmsg(sock_fd, &hdr[0].msg_hdr, flags);
if (status >= 0)
hdr[0].msg_len = status;
#endif
if (status < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not receive from fd %d : %s", sock_fd,
strerror(errno));
return;
}
for (i = 0; i < n; i++) {
hdr = ARR_GetElement(recv_headers, i);
process_message(&hdr->msg_hdr, hdr->msg_len, sock_fd);
}
/* Restore the buffers to their original state */
prepare_buffers(n);
} }
/* ================================================== */ /* ================================================== */
/* Send a packet to remote address from local address */ /* Send a packet to remote address from local address */
static int int
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr) NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx)
{ {
union sockaddr_in46 remote; union sockaddr_in46 remote;
struct msghdr msg; struct msghdr msg;
struct iovec iov; struct iovec iov;
char cmsgbuf[256]; struct cmsghdr *cmsg, cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
int cmsglen; int cmsglen;
socklen_t addrlen = 0; socklen_t addrlen = 0;
@@ -604,11 +748,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
} }
/* Don't set address with connected socket */ /* Don't set address with connected socket */
if (local_addr->sock_fd == server_sock_fd4 || if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
#ifdef FEAT_IPV6
local_addr->sock_fd == server_sock_fd6 ||
#endif
!separate_client_sockets) {
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port,
&remote.u); &remote.u);
if (!addrlen) if (!addrlen)
@@ -624,7 +764,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
} }
iov.iov_base = packet; iov.iov_base = packet;
iov.iov_len = packetlen; iov.iov_len = length;
msg.msg_iov = &iov; msg.msg_iov = &iov;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf; msg.msg_control = cmsgbuf;
@@ -634,7 +774,6 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
#ifdef HAVE_IN_PKTINFO #ifdef HAVE_IN_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) { if (local_addr->ip_addr.family == IPADDR_INET4) {
struct cmsghdr *cmsg;
struct in_pktinfo *ipi; struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
@@ -652,7 +791,6 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
#ifdef HAVE_IN6_PKTINFO #ifdef HAVE_IN6_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET6) { if (local_addr->ip_addr.family == IPADDR_INET6) {
struct cmsghdr *cmsg;
struct in6_pktinfo *ipi; struct in6_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
@@ -669,6 +807,11 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
} }
#endif #endif
#ifdef HAVE_LINUX_TIMESTAMPING
if (process_tx)
cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd);
#endif
msg.msg_controllen = cmsglen; msg.msg_controllen = cmsglen;
/* This is apparently required on some systems */ /* This is apparently required on some systems */
if (!cmsglen) if (!cmsglen)
@@ -682,18 +825,9 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
return 0; return 0;
} }
DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", packetlen, DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", length,
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd); UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
return 1; return 1;
} }
/* ================================================== */
/* Send a packet to a given address */
int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
return send_packet((void *) packet, length, remote_addr, local_addr);
}

View File

@@ -54,6 +54,7 @@ extern void NIO_CloseServerSocket(int sock_fd);
extern int NIO_IsServerSocket(int sock_fd); extern int NIO_IsServerSocket(int sock_fd);
/* Function to transmit a packet */ /* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx);
#endif /* GOT_NTP_IO_H */ #endif /* GOT_NTP_IO_H */

627
ntp_io_linux.c Normal file
View File

@@ -0,0 +1,627 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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.
*
**********************************************************************
=======================================================================
Functions for NTP I/O specific to Linux
*/
#include "config.h"
#include "sysincl.h"
#include <ifaddrs.h>
#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>
#include "array.h"
#include "conf.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
#include "ntp_sources.h"
#include "sched.h"
#include "sys_linux.h"
#include "util.h"
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
struct Interface {
char name[IF_NAMESIZE];
int if_index;
int phc_fd;
/* 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;
HCL_Instance clock;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
/* Array of Interfaces */
static ARR_Instance interfaces;
/* RX/TX and TX-specific timestamping socket options */
static int ts_flags;
static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* ================================================== */
static int
add_interface(const char *name)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_index, phc_fd;
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))
return 1;
}
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
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)) {
close(sock_fd);
return 0;
}
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
close(sock_fd);
return 0;
}
if_index = req.ifr_ifindex;
ts_info.cmd = ETHTOOL_GET_TS_INFO;
req.ifr_data = (char *)&ts_info;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return 0;
}
ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON;
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
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;
}
close(sock_fd);
phc_index = ts_info.phc_index;
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
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);
iface->if_index = if_index;
iface->phc_fd = phc_fd;
/* 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->clock = HCL_CreateInstance();
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", name);
return 1;
}
/* ================================================== */
static int
add_all_interfaces(void)
{
struct ifaddrs *ifaddr, *ifa;
int r;
if (getifaddrs(&ifaddr)) {
DEBUG_LOG(LOGF_NtpIOLinux, "getifaddrs() failed : %s", strerror(errno));
return 0;
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (add_interface(ifa->ifa_name))
r = 1;
}
freeifaddrs(ifaddr);
/* Return success if at least one interface was added */
return r;
}
/* ================================================== */
static void
update_interface_speed(struct Interface *iface)
{
struct ethtool_cmd cmd;
struct ifreq req;
int sock_fd;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return;
memset(&req, 0, sizeof (req));
memset(&cmd, 0, sizeof (cmd));
snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface->name);
cmd.cmd = ETHTOOL_GSET;
req.ifr_data = (char *)&cmd;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return;
}
close(sock_fd);
iface->link_speed = ethtool_cmd_speed(&cmd);
}
/* ================================================== */
void
NIO_Linux_Initialise(void)
{
ARR_Instance config_hwts_ifaces;
char *if_name;
unsigned int i;
int wildcard, hwts;
interfaces = ARR_CreateInstance(sizeof (struct Interface));
config_hwts_ifaces = CNF_GetHwTsInterfaces();
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
for (i = wildcard = 0; i < ARR_GetSize(config_hwts_ifaces); i++) {
if (!strcmp("*", *(char **)ARR_GetElement(config_hwts_ifaces, i)))
wildcard = 1;
}
if (!wildcard && ARR_GetSize(config_hwts_ifaces)) {
for (i = 0; i < ARR_GetSize(config_hwts_ifaces); i++) {
if_name = *(char **)ARR_GetElement(config_hwts_ifaces, i);
if (!add_interface(if_name))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", if_name);
}
hwts = 1;
} else if (wildcard && add_all_interfaces()) {
hwts = 1;
} else {
hwts = 0;
}
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;
}
/* Enable IP_PKTINFO in messages looped back to the error queue */
ts_flags |= SOF_TIMESTAMPING_OPT_CMSG;
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
}
/* ================================================== */
void
NIO_Linux_Finalise(void)
{
struct Interface *iface;
unsigned int i;
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
ARR_DestroyInstance(interfaces);
}
/* ================================================== */
int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
{
int val, flags;
if (!ts_flags)
return 0;
/* Enable SCM_TIMESTAMPING control messages and the socket's error queue in
order to receive our transmitted packets with more accurate timestamps */
val = 1;
flags = ts_flags;
if (client_only || permanent_ts_options)
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");
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");
ts_flags = 0;
return 0;
}
*events |= SCH_FILE_EXCEPTION;
return 1;
}
/* ================================================== */
static int
get_phc_sample(int phc_fd, struct timespec *phc_ts, struct timespec *local_ts, double *p_delay)
{
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;
/* 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(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
return 0;
}
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;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
struct Interface *iface;
unsigned int i;
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
if (iface->if_index != if_index)
continue;
return iface;
}
return NULL;
}
/* ================================================== */
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family)
{
struct timespec sample_phc_ts, sample_local_ts;
double sample_delay, rx_correction;
int l2_length;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!get_phc_sample(iface->phc_fd, &sample_phc_ts, &sample_local_ts, &sample_delay))
return;
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
sample_delay / 2.0);
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. */
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;
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
}
if (!HCL_CookTime(iface->clock, hw_ts, &local_ts->ts, &local_ts->err))
return;
local_ts->source = NTP_TS_HARDWARE;
}
/* ================================================== */
/* Extract UDP data from a layer 2 message. Supported is Ethernet
with optional VLAN tags. */
static int
extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
{
unsigned char *msg_start = msg;
union sockaddr_in46 addr;
remote_addr->ip_addr.family = IPADDR_UNSPEC;
remote_addr->port = 0;
/* Skip MACs */
if (len < 12)
return 0;
len -= 12, msg += 12;
/* Skip VLAN tag(s) if present */
while (len >= 4 && msg[0] == 0x81 && msg[1] == 0x00)
len -= 4, msg += 4;
/* Skip IPv4 or IPv6 ethertype */
if (len < 2 || !((msg[0] == 0x08 && msg[1] == 0x00) ||
(msg[0] == 0x86 && msg[1] == 0xdd)))
return 0;
len -= 2, msg += 2;
/* Parse destination address and port from IPv4/IPv6 and UDP headers */
if (len >= 20 && msg[0] >> 4 == 4) {
int ihl = (msg[0] & 0xf) * 4;
if (len < ihl + 8 || msg[9] != 17)
return 0;
memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t));
addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2);
addr.in4.sin_family = AF_INET;
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;
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
addr.in6.sin6_port = *(uint16_t *)(msg + 40 + 2);
addr.in6.sin6_family = AF_INET6;
len -= 48, msg += 48;
#endif
} else {
return 0;
}
UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port);
/* Move the message to fix alignment of its fields */
if (len > 0)
memmove(msg_start, msg, len);
return 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)
{
struct Interface *iface;
struct cmsghdr *cmsg;
int is_tx, l2_length;
is_tx = hdr->msg_flags & MSG_ERRQUEUE;
iface = NULL;
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
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 {
iface = get_interface(if_index);
if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
remote_addr->ip_addr.family);
} else {
DEBUG_LOG(LOGF_NtpIOLinux, "HW clock not found for interface %d", if_index);
}
}
}
if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
(cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
struct sock_extended_err err;
memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
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");
/* Drop the message */
return 1;
}
}
}
/* Return the message if it's not received from the error queue */
if (!is_tx)
return 0;
/* The data from the error queue includes all layers up to UDP. We have to
extract the UDP data and also the destination address with port as there
currently doesn't seem to be a better way to get them both. */
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",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
sock_fd, if_index, local_ts->source);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && length) {
if (remote_addr->ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - length;
else if (remote_addr->ip_addr.family == IPADDR_INET6)
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");
return 1;
}
if (length < NTP_NORMAL_PACKET_LENGTH)
return 1;
NSR_ProcessTx(remote_addr, local_addr, local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
return 1;
}
/* ================================================== */
int
NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen;
/* 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);
memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMPING;
cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags));
memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags));
return cmsglen;
}

37
ntp_io_linux.h Normal file
View File

@@ -0,0 +1,37 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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 is the header file for the Linux-specific NTP socket I/O bits.
*/
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_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);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);

380
ntp_signd.c Normal file
View File

@@ -0,0 +1,380 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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.
*
**********************************************************************
=======================================================================
Support for MS-SNTP authentication in Samba (ntp_signd)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "conf.h"
#include "logging.h"
#include "ntp_io.h"
#include "ntp_signd.h"
#include "sched.h"
#include "util.h"
/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
#define SIGND_VERSION 0
typedef enum {
SIGN_TO_CLIENT = 0,
ASK_SERVER_TO_SIGN = 1,
CHECK_SERVER_SIGNATURE = 2,
SIGNING_SUCCESS = 3,
SIGNING_FAILURE = 4,
} SigndOp;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint16_t packet_id;
uint16_t _pad;
uint32_t key_id;
NTP_Packet packet_to_sign;
} SigndRequest;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint32_t packet_id;
NTP_Packet signed_packet;
} SigndResponse;
typedef struct {
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
int sent;
int received;
int request_length;
struct timespec request_ts;
SigndRequest request;
SigndResponse response;
} SignInstance;
/* As the communication with ntp_signd is asynchronous, incoming packets are
saved in a queue in order to avoid loss when they come in bursts */
#define MAX_QUEUE_LENGTH 16U
#define NEXT_QUEUE_INDEX(index) (((index) + 1) % MAX_QUEUE_LENGTH)
#define IS_QUEUE_EMPTY() (queue_head == queue_tail)
/* Fixed-size array of SignInstance */
static ARR_Instance queue;
static unsigned int queue_head;
static unsigned int queue_tail;
#define INVALID_SOCK_FD -1
/* Unix domain socket connected to ntp_signd */
static int sock_fd;
#define MIN_AUTH_DELAY 1.0e-5
#define MAX_AUTH_DELAY 1.0e-2
/* Average time needed for signing one packet. This is used to adjust the
transmit timestamp in NTP packets. The timestamp won't be very accurate as
the delay is variable, but it should be good enough for MS-SNTP clients. */
static double auth_delay;
/* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled;
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static void
close_socket(void)
{
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
sock_fd = INVALID_SOCK_FD;
/* Empty the queue */
queue_head = queue_tail = 0;
}
/* ================================================== */
static int
open_socket(void)
{
struct sockaddr_un s;
if (sock_fd >= 0)
return 1;
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
DEBUG_LOG(LOGF_NtpSignd, "Could not open signd socket : %s", strerror(errno));
return 0;
}
UTI_FdSetCloexec(sock_fd);
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
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");
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));
close_socket();
return 0;
}
DEBUG_LOG(LOGF_NtpSignd, "Connected to signd");
return 1;
}
/* ================================================== */
static void
process_response(SignInstance *inst)
{
struct timespec ts;
double delay;
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid response ID");
return;
}
if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
DEBUG_LOG(LOGF_NtpSignd, "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");
return;
}
SCH_GetLastEventTime(NULL, NULL, &ts);
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
DEBUG_LOG(LOGF_NtpSignd, "Signing succeeded (delay %f)", delay);
/* Send the signed NTP packet */
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
ntohl(inst->response.length) + sizeof (inst->response.length) -
offsetof(SigndResponse, signed_packet), 0);
/* Update exponential moving average of the authentication delay */
delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
auth_delay += 0.1 * (delay - auth_delay);
}
/* ================================================== */
static void
read_write_socket(int sock_fd, int event, void *anything)
{
SignInstance *inst;
uint32_t response_length;
int s;
inst = ARR_GetElement(queue, queue_head);
if (event == SCH_FILE_OUTPUT) {
assert(!IS_QUEUE_EMPTY());
assert(inst->sent < inst->request_length);
if (!inst->sent)
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
s = send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0);
if (s < 0) {
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
close_socket();
return;
}
DEBUG_LOG(LOGF_NtpSignd, "Sent %d bytes to signd", s);
inst->sent += s;
/* Try again later if the request is not complete yet */
if (inst->sent < inst->request_length)
return;
/* Disable output and wait for a response */
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
}
if (event == SCH_FILE_INPUT) {
if (IS_QUEUE_EMPTY()) {
DEBUG_LOG(LOGF_NtpSignd, "Unexpected signd response");
close_socket();
return;
}
assert(inst->received < sizeof (inst->response));
s = recv(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0);
if (s <= 0) {
if (s < 0)
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
else
DEBUG_LOG(LOGF_NtpSignd, "signd socket closed");
close_socket();
return;
}
DEBUG_LOG(LOGF_NtpSignd, "Received %d bytes from signd", s);
inst->received += s;
if (inst->received < sizeof (inst->response.length))
return;
response_length = ntohl(inst->response.length) + sizeof (inst->response.length);
if (response_length < offsetof(SigndResponse, signed_packet) ||
response_length > sizeof (SigndResponse)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid response length");
close_socket();
return;
}
/* Wait for more data if not complete yet */
if (inst->received < response_length)
return;
process_response(inst);
/* 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);
}
}
/* ================================================== */
void
NSD_Initialise()
{
sock_fd = INVALID_SOCK_FD;
auth_delay = MIN_AUTH_DELAY;
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
if (!enabled)
return;
queue = ARR_CreateInstance(sizeof (SignInstance));
ARR_SetSize(queue, MAX_QUEUE_LENGTH);
queue_head = queue_tail = 0;
LOG(LOGS_INFO, LOGF_NtpSignd, "MS-SNTP authentication enabled");
}
/* ================================================== */
void
NSD_Finalise()
{
if (!enabled)
return;
if (sock_fd != INVALID_SOCK_FD)
close_socket();
ARR_DestroyInstance(queue);
}
/* ================================================== */
extern int NSD_GetAuthDelay(uint32_t key_id)
{
return 1.0e9 * auth_delay;
}
/* ================================================== */
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
SignInstance *inst;
if (!enabled) {
DEBUG_LOG(LOGF_NtpSignd, "signd disabled");
return 0;
}
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
DEBUG_LOG(LOGF_NtpSignd, "signd queue full");
return 0;
}
if (length != NTP_NORMAL_PACKET_LENGTH) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid packet length");
return 0;
}
if (!open_socket())
return 0;
inst = ARR_GetElement(queue, queue_tail);
inst->remote_addr = *remote_addr;
inst->local_addr = *local_addr;
inst->sent = 0;
inst->received = 0;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
/* The length field doesn't include itself */
inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
inst->request.version = htonl(SIGND_VERSION);
inst->request.op = htonl(SIGN_TO_CLIENT);
inst->request.packet_id = htons(queue_tail);
inst->request._pad = 0;
inst->request.key_id = htonl(key_id);
memcpy(&inst->request.packet_to_sign, packet, length);
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
DEBUG_LOG(LOGF_NtpSignd, "Packet added to signd queue (%u:%u)",
queue_head, queue_tail);
return 1;
}

44
ntp_signd.h Normal file
View File

@@ -0,0 +1,44 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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 for MS-SNTP authentication via Samba (ntp_signd) */
#ifndef GOT_NTP_SIGND_H
#define GOT_NTP_SIGND_H
#include "addressing.h"
#include "ntp.h"
/* Initialisation function */
extern void NSD_Initialise(void);
/* Finalisation function */
extern void NSD_Finalise(void);
/* Function to get an estimate of delay due to signing */
extern int NSD_GetAuthDelay(uint32_t key_id);
/* Function to sign an NTP packet and send it */
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
#endif

View File

@@ -117,8 +117,8 @@ static void rehash_records(void);
static void clean_source_record(SourceRecord *record); static void clean_source_record(SourceRecord *record);
static void static void
slew_sources(struct timeval *raw, slew_sources(struct timespec *raw,
struct timeval *cooked, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -680,8 +680,8 @@ resolve_source_replacement(SourceRecord *record)
void void
NSR_HandleBadSource(IPAddr *address) NSR_HandleBadSource(IPAddr *address)
{ {
static struct timeval last_replacement; static struct timespec last_replacement;
struct timeval now; struct timespec now;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
SourceRecord *record; SourceRecord *record;
int slot, found; int slot, found;
@@ -702,7 +702,7 @@ NSR_HandleBadSource(IPAddr *address)
/* Don't resolve names too frequently */ /* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now); SCH_GetLastEventTime(NULL, NULL, &now);
UTI_DiffTimevalsToDouble(&diff, &now, &last_replacement); diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) { if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG(LOGF_NtpSources, "replacement postponed"); DEBUG_LOG(LOGF_NtpSources, "replacement postponed");
return; return;
@@ -776,7 +776,8 @@ NSR_GetLocalRefid(IPAddr *address)
/* This routine is called by ntp_io when a new packet arrives off the network, /* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */ possibly with an authentication tail */
void void
NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{ {
SourceRecord *record; SourceRecord *record;
struct SourcePool *pool; struct SourcePool *pool;
@@ -788,11 +789,11 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
if (found == 2) { /* Must match IP address AND port number */ if (found == 2) { /* Must match IP address AND port number */
record = get_record(slot); record = get_record(slot);
if (!NCR_ProcessKnown(message, now, now_err, record->data, local_addr, length)) if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
return; return;
if (record->tentative) { if (record->tentative) {
/* This was the first valid reply from the source */ /* This was the first good reply from the source */
record->tentative = 0; record->tentative = 0;
if (record->pool != INVALID_POOL) { if (record->pool != INVALID_POOL) {
@@ -809,15 +810,34 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
} }
} }
} else { } else {
NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length); NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
}
}
/* ================================================== */
void
NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
{
SourceRecord *record;
int slot, found;
find_slot(remote_addr, &slot, &found);
if (found == 2) { /* Must match IP address AND port number */
record = get_record(slot);
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
} else {
NCR_ProcessTxUnknown(remote_addr, local_addr, tx_ts, message, length);
} }
} }
/* ================================================== */ /* ================================================== */
static void static void
slew_sources(struct timeval *raw, slew_sources(struct timespec *raw,
struct timeval *cooked, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -831,6 +851,7 @@ slew_sources(struct timeval *raw,
if (record->remote_addr) { if (record->remote_addr) {
if (change_type == LCL_ChangeUnknownStep) { if (change_type == LCL_ChangeUnknownStep) {
NCR_ResetInstance(record->data); NCR_ResetInstance(record->data);
NCR_ResetPoll(record->data);
} else { } else {
NCR_SlewTimes(record->data, cooked, dfreq, doffset); NCR_SlewTimes(record->data, cooked, dfreq, doffset);
} }
@@ -1083,7 +1104,7 @@ NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
identify the source record. */ identify the source record. */
void void
NSR_ReportSource(RPT_SourceReport *report, struct timeval *now) NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{ {
NTP_Remote_Address rem_addr; NTP_Remote_Address rem_addr;
int slot, found; int slot, found;
@@ -1099,6 +1120,26 @@ NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
} }
} }
/* ================================================== */
/* The ip address is assumed to be completed on input, that is how we
identify the source record. */
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
NTP_Remote_Address rem_addr;
int slot, found;
rem_addr.ip_addr = report->remote_addr;
rem_addr.port = 0;
find_slot(&rem_addr, &slot, &found);
if (!found)
return 0;
NCR_GetNTPReport(get_record(slot)->data, report);
return 1;
}
/* ================================================== */ /* ================================================== */
void void

View File

@@ -87,7 +87,13 @@ extern void NSR_RefreshAddresses(void);
extern uint32_t NSR_GetLocalRefid(IPAddr *address); extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */ /* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called by ntp_io when a packet was sent to the network and
an accurate transmit timestamp was captured */
extern void NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Initialisation function */ /* Initialisation function */
extern void NSR_Initialise(void); extern void NSR_Initialise(void);
@@ -121,7 +127,9 @@ extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address); extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
extern void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now); extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report); extern void NSR_GetActivityReport(RPT_ActivityReport *report);

View File

@@ -82,8 +82,8 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENYALL */ REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENYALL */
REQ_LENGTH_ENTRY(ac_check, null), /* ACCHECK */ REQ_LENGTH_ENTRY(ac_check, null), /* ACCHECK */
REQ_LENGTH_ENTRY(ac_check, null), /* CMDACCHECK */ REQ_LENGTH_ENTRY(ac_check, null), /* CMDACCHECK */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER */ { 0, 0 }, /* ADD_SERVER */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER */ { 0, 0 }, /* ADD_PEER */
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */ REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */ REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */ REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */
@@ -113,6 +113,9 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(client_accesses_by_index, REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */ 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 */
}; };
static const uint16_t reply_lengths[] = { static const uint16_t reply_lengths[] = {
@@ -132,6 +135,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */ RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */ RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
}; };
/* ================================================== */ /* ================================================== */

View File

@@ -48,7 +48,7 @@ extern RefclockDriver RCL_PHC_driver;
struct FilterSample { struct FilterSample {
double offset; double offset;
double dispersion; double dispersion;
struct timeval sample_time; struct timespec sample_time;
}; };
struct MedianFilter { struct MedianFilter {
@@ -77,6 +77,7 @@ struct RCL_Instance_Record {
int leap_status; int leap_status;
int pps_rate; int pps_rate;
int pps_active; int pps_active;
int max_lock_age;
struct MedianFilter filter; struct MedianFilter filter;
uint32_t ref_id; uint32_t ref_id;
uint32_t lock_ref; uint32_t lock_ref;
@@ -92,23 +93,24 @@ static ARR_Instance refclocks;
static LOG_FileID logfileid; static LOG_FileID logfileid;
static int valid_sample_time(RCL_Instance instance, struct timeval *tv); static int valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked);
static int pps_stratum(RCL_Instance instance, struct timeval *tv); static int pps_stratum(RCL_Instance instance, struct timespec *ts);
static void poll_timeout(void *arg); static void poll_timeout(void *arg);
static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything); double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything); static void add_dispersion(double dispersion, void *anything);
static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion); 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_init(struct MedianFilter *filter, int length, double max_dispersion);
static void filter_fini(struct MedianFilter *filter); static void filter_fini(struct MedianFilter *filter);
static void filter_reset(struct MedianFilter *filter); static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter); static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion); 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 timeval *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_select_samples(struct MedianFilter *filter);
static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion); 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 timeval *when, double dfreq, double doffset); 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 void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
static RCL_Instance static RCL_Instance
@@ -201,6 +203,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->leap_status = LEAP_Normal; inst->leap_status = LEAP_Normal;
inst->pps_rate = params->pps_rate; inst->pps_rate = params->pps_rate;
inst->pps_active = 0; inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age;
inst->lock_ref = params->lock_ref_id; inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset; inst->offset = params->offset;
inst->delay = params->delay; inst->delay = params->delay;
@@ -299,7 +302,7 @@ RCL_StartRefclocks(void)
} }
void void
RCL_ReportSource(RPT_SourceReport *report, struct timeval *now) RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
{ {
unsigned int i; unsigned int i;
uint32_t ref_id; uint32_t ref_id;
@@ -361,18 +364,18 @@ RCL_GetDriverOption(RCL_Instance instance, char *name)
} }
int int
RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap) RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{ {
double correction, dispersion; double correction, dispersion;
struct timeval cooked_time; struct timespec cooked_time;
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion); LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time); UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision; dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */ /* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) || if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time)) !valid_sample_time(instance, sample_time, &cooked_time))
return 0; return 0;
switch (leap) { switch (leap) {
@@ -399,20 +402,20 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
} }
int int
RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second) RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
{ {
double correction, dispersion, offset; double correction, dispersion, offset;
struct timeval cooked_time; struct timespec cooked_time;
int rate; int rate;
NTP_Leap leap; NTP_Leap leap;
leap = LEAP_Normal; leap = LEAP_Normal;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion); LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time); UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
dispersion += instance->precision; dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) || if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
!valid_sample_time(instance, pulse_time)) !valid_sample_time(instance, pulse_time, &cooked_time))
return 0; return 0;
rate = instance->pps_rate; rate = instance->pps_rate;
@@ -429,7 +432,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
if (instance->lock_ref != -1) { if (instance->lock_ref != -1) {
RCL_Instance lock_refclock; RCL_Instance lock_refclock;
struct timeval ref_sample_time; struct timespec ref_sample_time;
double sample_diff, ref_offset, ref_dispersion, shift; double sample_diff, ref_offset, ref_dispersion, shift;
lock_refclock = get_refclock(instance->lock_ref); lock_refclock = get_refclock(instance->lock_ref);
@@ -442,8 +445,8 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter); ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time); sample_diff = UTI_DiffTimespecsToDouble(&cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= 2.0 / rate) { if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f", DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
sample_diff); sample_diff);
return 0; return 0;
@@ -468,7 +471,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f", DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff); second, offset, ref_offset - offset, sample_diff);
} else { } else {
struct timeval ref_time; struct timespec ref_time;
int is_synchronised, stratum; int is_synchronised, stratum;
double root_delay, root_dispersion, distance; double root_delay, root_dispersion, distance;
uint32_t ref_id; uint32_t ref_id;
@@ -503,25 +506,32 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
} }
static int static int
valid_sample_time(RCL_Instance instance, struct timeval *tv) valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked)
{ {
struct timeval raw_time; struct timespec now_raw, last_sample_time;
double diff; double diff, last_offset, last_dispersion;
LCL_ReadRawTime(&raw_time); LCL_ReadRawTime(&now_raw);
UTI_DiffTimevalsToDouble(&diff, &raw_time, tv); diff = UTI_DiffTimespecsToDouble(&now_raw, raw);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f tv=%s", if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
UTI_RefidToString(instance->ref_id), diff, UTI_TimevalToString(tv)); (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));
return 0; return 0;
} }
return 1; return 1;
} }
static int static int
pps_stratum(RCL_Instance instance, struct timeval *tv) pps_stratum(RCL_Instance instance, struct timespec *ts)
{ {
struct timeval ref_time; struct timespec ref_time;
int is_synchronised, stratum; int is_synchronised, stratum;
unsigned int i; unsigned int i;
double root_delay, root_dispersion; double root_delay, root_dispersion;
@@ -529,7 +539,7 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
uint32_t ref_id; uint32_t ref_id;
RCL_Instance refclock; RCL_Instance refclock;
REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum, REF_GetReferenceParams(ts, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion); &ref_id, &ref_time, &root_delay, &root_dispersion);
/* Don't change our stratum if the local reference is active /* Don't change our stratum if the local reference is active
@@ -566,7 +576,7 @@ poll_timeout(void *arg)
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) { if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion; double offset, dispersion;
struct timeval sample_time; struct timespec sample_time;
int sample_ok, stratum; int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion); sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
@@ -594,7 +604,7 @@ poll_timeout(void *arg)
} }
static void static void
slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything) double doffset, LCL_ChangeType change_type, void *anything)
{ {
unsigned int i; unsigned int i;
@@ -617,7 +627,7 @@ add_dispersion(double dispersion, void *anything)
} }
static void static void
log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion) log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
{ {
char sync_stats[4] = {'N', '+', '-', '?'}; char sync_stats[4] = {'N', '+', '-', '?'};
@@ -627,7 +637,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
if (!filtered) { if (!filtered) {
LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e", LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec), UTI_TimeToLogForm(sample_time->tv_sec),
(int)sample_time->tv_usec, (int)sample_time->tv_nsec / 1000,
UTI_RefidToString(instance->ref_id), UTI_RefidToString(instance->ref_id),
instance->driver_polled, instance->driver_polled,
sync_stats[instance->leap_status], sync_stats[instance->leap_status],
@@ -638,7 +648,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
} else { } else {
LOG_FileWrite(logfileid, "%s.%06d %-5s - %1c - - %13.6e %10.3e", LOG_FileWrite(logfileid, "%s.%06d %-5s - %1c - - %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec), UTI_TimeToLogForm(sample_time->tv_sec),
(int)sample_time->tv_usec, (int)sample_time->tv_nsec / 1000,
UTI_RefidToString(instance->ref_id), UTI_RefidToString(instance->ref_id),
sync_stats[instance->leap_status], sync_stats[instance->leap_status],
cooked_offset, cooked_offset,
@@ -691,7 +701,7 @@ filter_get_avg_sample_dispersion(struct MedianFilter *filter)
} }
static void static void
filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion) filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
{ {
filter->index++; filter->index++;
filter->index %= filter->length; filter->index %= filter->length;
@@ -704,11 +714,11 @@ filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
filter->samples[filter->index].dispersion = dispersion; filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f", DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimevalToString(sample_time), offset, dispersion); filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
} }
static int static int
filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion) filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{ {
if (filter->last < 0) if (filter->last < 0)
return 0; return 0;
@@ -719,6 +729,12 @@ filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time,
return 1; return 1;
} }
static int
filter_get_samples(struct MedianFilter *filter)
{
return filter->used;
}
static const struct FilterSample *tmp_sorted_array; static const struct FilterSample *tmp_sorted_array;
static int static int
@@ -821,7 +837,7 @@ filter_select_samples(struct MedianFilter *filter)
} }
static int static int
filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion) filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{ {
struct FilterSample *s, *ls; struct FilterSample *s, *ls;
int i, n, dof; int i, n, dof;
@@ -838,7 +854,7 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
s = &filter->samples[filter->selected[i]]; s = &filter->samples[filter->selected[i]];
UTI_DiffTimevalsToDouble(&filter->x_data[i], &s->sample_time, &ls->sample_time); filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
filter->y_data[i] = s->offset; filter->y_data[i] = s->offset;
filter->w_data[i] = s->dispersion; filter->w_data[i] = s->dispersion;
} }
@@ -913,7 +929,7 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
if (d < e) if (d < e)
d = e; d = e;
UTI_AddDoubleToTimeval(&ls->sample_time, x, sample_time); UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
*offset = y; *offset = y;
*dispersion = d; *dispersion = d;
@@ -923,15 +939,26 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
} }
static void static void
filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset) filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
{ {
int i; int i, first, last;
double delta_time; double delta_time;
struct timeval *sample; struct timespec *sample;
for (i = 0; i < filter->used; i++) { 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; sample = &filter->samples[i].sample_time;
UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset); UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
filter->samples[i].offset -= delta_time; filter->samples[i].offset -= delta_time;
} }
} }

View File

@@ -41,6 +41,7 @@ typedef struct {
int min_samples; int min_samples;
int max_samples; int max_samples;
int sel_options; int sel_options;
int max_lock_age;
uint32_t ref_id; uint32_t ref_id;
uint32_t lock_ref_id; uint32_t lock_ref_id;
double offset; double offset;
@@ -61,14 +62,14 @@ extern void RCL_Initialise(void);
extern void RCL_Finalise(void); extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params); extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void); extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now); extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
/* functions used by drivers */ /* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data); extern void RCL_SetDriverData(RCL_Instance instance, void *data);
extern void *RCL_GetDriverData(RCL_Instance instance); extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance); extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name); extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap); extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second); extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
#endif #endif

View File

@@ -56,11 +56,6 @@ struct phc_reading {
struct timespec sys_ts2; struct timespec sys_ts2;
}; };
static double diff_ts(struct timespec *ts1, struct timespec *ts2)
{
return (ts1->tv_sec - ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
}
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n) static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
{ {
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES #if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
@@ -145,7 +140,6 @@ static void phc_finalise(RCL_Instance instance)
static int phc_poll(RCL_Instance instance) static int phc_poll(RCL_Instance instance)
{ {
struct phc_reading readings[NUM_READINGS]; struct phc_reading readings[NUM_READINGS];
struct timeval tv;
double offset = 0.0, delay, best_delay = 0.0; double offset = 0.0, delay, best_delay = 0.0;
int i, phc_fd, best; int i, phc_fd, best;
@@ -163,7 +157,7 @@ static int phc_poll(RCL_Instance instance)
/* Find the fastest reading */ /* Find the fastest reading */
for (i = 0; i < NUM_READINGS; i++) { for (i = 0; i < NUM_READINGS; i++) {
delay = diff_ts(&readings[i].sys_ts2, &readings[i].sys_ts1); delay = UTI_DiffTimespecsToDouble(&readings[i].sys_ts2, &readings[i].sys_ts1);
if (!i || best_delay > delay) { if (!i || best_delay > delay) {
best = i; best = i;
@@ -171,13 +165,12 @@ static int phc_poll(RCL_Instance instance)
} }
} }
offset = diff_ts(&readings[best].phc_ts, &readings[best].sys_ts2) + best_delay / 2.0; offset = UTI_DiffTimespecsToDouble(&readings[best].phc_ts, &readings[best].sys_ts2) +
tv.tv_sec = readings[best].sys_ts2.tv_sec; best_delay / 2.0;
tv.tv_usec = readings[best].sys_ts2.tv_nsec / 1000;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay); DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
return RCL_AddSample(instance, &tv, offset, LEAP_Normal); return RCL_AddSample(instance, &readings[best].sys_ts2, offset, LEAP_Normal);
} }
RefclockDriver RCL_PHC_driver = { RefclockDriver RCL_PHC_driver = {

View File

@@ -124,7 +124,6 @@ static int pps_poll(RCL_Instance instance)
{ {
struct pps_instance *pps; struct pps_instance *pps;
struct timespec ts; struct timespec ts;
struct timeval tv;
pps_info_t pps_info; pps_info_t pps_info;
pps_seq_t seq; pps_seq_t seq;
@@ -146,17 +145,15 @@ static int pps_poll(RCL_Instance instance)
ts = pps_info.clear_timestamp; ts = pps_info.clear_timestamp;
} }
if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) { if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%lu.%09lu", DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%s",
seq, ts.tv_sec, ts.tv_nsec); seq, UTI_TimespecToString(&ts));
return 0; return 0;
} }
pps->last_seq = seq; pps->last_seq = seq;
tv.tv_sec = ts.tv_sec;
tv.tv_usec = ts.tv_nsec / 1000;
return RCL_AddPulse(instance, &tv, ts.tv_nsec / 1e9); return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
} }
RefclockDriver RCL_PPS_driver = { RefclockDriver RCL_PPS_driver = {

View File

@@ -90,7 +90,7 @@ static void shm_finalise(RCL_Instance instance)
static int shm_poll(RCL_Instance instance) static int shm_poll(RCL_Instance instance)
{ {
struct timeval tv; struct timespec receive_ts, clock_ts;
struct shmTime t, *shm; struct shmTime t, *shm;
double offset; double offset;
@@ -107,17 +107,23 @@ static int shm_poll(RCL_Instance instance)
shm->valid = 0; shm->valid = 0;
tv.tv_sec = t.receiveTimeStampSec; receive_ts.tv_sec = t.receiveTimeStampSec;
tv.tv_usec = t.receiveTimeStampUSec; clock_ts.tv_sec = t.clockTimeStampSec;
offset = t.clockTimeStampSec - t.receiveTimeStampSec;
if (t.clockTimeStampNSec / 1000 == t.clockTimeStampUSec && if (t.clockTimeStampNSec / 1000 == t.clockTimeStampUSec &&
t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec) t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec) {
offset += (t.clockTimeStampNSec - t.receiveTimeStampNSec) * 1e-9; receive_ts.tv_nsec = t.receiveTimeStampNSec;
else clock_ts.tv_nsec = t.clockTimeStampNSec;
offset += (t.clockTimeStampUSec - t.receiveTimeStampUSec) * 1e-6; } else {
receive_ts.tv_nsec = 1000 * t.receiveTimeStampUSec;
clock_ts.tv_nsec = 1000 * t.clockTimeStampUSec;
}
return RCL_AddSample(instance, &tv, offset, t.leap); UTI_NormaliseTimespec(&clock_ts);
UTI_NormaliseTimespec(&receive_ts);
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
} }
RefclockDriver RCL_SHM_driver = { RefclockDriver RCL_SHM_driver = {

View File

@@ -57,14 +57,14 @@ struct sock_sample {
int magic; int magic;
}; };
static void read_sample(void *anything) static void read_sample(int sockfd, int event, void *anything)
{ {
struct sock_sample sample; struct sock_sample sample;
struct timespec ts;
RCL_Instance instance; RCL_Instance instance;
int sockfd, s; int s;
instance = (RCL_Instance)anything; instance = (RCL_Instance)anything;
sockfd = (long)RCL_GetDriverData(instance);
s = recv(sockfd, &sample, sizeof (sample), 0); s = recv(sockfd, &sample, sizeof (sample), 0);
@@ -86,10 +86,13 @@ static void read_sample(void *anything)
return; return;
} }
UTI_TimevalToTimespec(&sample.tv, &ts);
UTI_NormaliseTimespec(&ts);
if (sample.pulse) { if (sample.pulse) {
RCL_AddPulse(instance, &sample.tv, sample.offset); RCL_AddPulse(instance, &ts, sample.offset);
} else { } else {
RCL_AddSample(instance, &sample.tv, sample.offset, sample.leap); RCL_AddSample(instance, &ts, sample.offset, sample.leap);
} }
} }
@@ -122,7 +125,7 @@ static int sock_initialise(RCL_Instance instance)
} }
RCL_SetDriverData(instance, (void *)(long)sockfd); RCL_SetDriverData(instance, (void *)(long)sockfd);
SCH_AddInputFileHandler(sockfd, read_sample, instance); SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance);
return 1; return 1;
} }
@@ -131,7 +134,7 @@ static void sock_finalise(RCL_Instance instance)
int sockfd; int sockfd;
sockfd = (long)RCL_GetDriverData(instance); sockfd = (long)RCL_GetDriverData(instance);
SCH_RemoveInputFileHandler(sockfd); SCH_RemoveFileHandler(sockfd);
close(sockfd); close(sockfd);
} }

View File

@@ -52,7 +52,7 @@ static int our_leap_sec;
static int our_stratum; static int our_stratum;
static uint32_t our_ref_id; static uint32_t our_ref_id;
static IPAddr our_ref_ip; static IPAddr our_ref_ip;
struct timeval our_ref_time; static struct timespec our_ref_time;
static double our_skew; static double our_skew;
static double our_residual_freq; static double our_residual_freq;
static double our_root_delay; static double our_root_delay;
@@ -136,7 +136,7 @@ static int next_fb_drift;
static SCH_TimeoutID fb_drift_timeout_id; static SCH_TimeoutID fb_drift_timeout_id;
/* Timestamp of last reference update */ /* Timestamp of last reference update */
static struct timeval last_ref_update; static struct timespec last_ref_update;
static double last_ref_update_interval; static double last_ref_update_interval;
/* ================================================== */ /* ================================================== */
@@ -147,23 +147,22 @@ static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */ /* ================================================== */
static void static void
handle_slew(struct timeval *raw, handle_slew(struct timespec *raw,
struct timeval *cooked, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
void *anything) void *anything)
{ {
double delta; double delta;
struct timeval now; struct timespec now;
UTI_AdjustTimeval(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset); UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) { if (change_type == LCL_ChangeUnknownStep) {
last_ref_update.tv_sec = 0; UTI_ZeroTimespec(&last_ref_update);
last_ref_update.tv_usec = 0;
} else if (last_ref_update.tv_sec) { } else if (last_ref_update.tv_sec) {
UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset); UTI_AdjustTimespec(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
} }
/* When the clock was stepped, check if that doesn't change our leap status /* When the clock was stepped, check if that doesn't change our leap status
@@ -267,8 +266,7 @@ REF_Initialise(void)
fb_drift_timeout_id = 0; fb_drift_timeout_id = 0;
} }
last_ref_update.tv_sec = 0; UTI_ZeroTimespec(&last_ref_update);
last_ref_update.tv_usec = 0;
last_ref_update_interval = 0.0; last_ref_update_interval = 0.0;
LCL_AddParameterChangeHandler(handle_slew, NULL); LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -468,16 +466,16 @@ fb_drift_timeout(void *arg)
/* ================================================== */ /* ================================================== */
static void static void
schedule_fb_drift(struct timeval *now) schedule_fb_drift(struct timespec *now)
{ {
int i, c, secs; int i, c, secs;
double unsynchronised; double unsynchronised;
struct timeval when; struct timespec when;
if (fb_drift_timeout_id) if (fb_drift_timeout_id)
return; /* already scheduled */ return; /* already scheduled */
UTI_DiffTimevalsToDouble(&unsynchronised, now, &last_ref_update); unsynchronised = UTI_DiffTimespecsToDouble(now, &last_ref_update);
for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) { for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) {
secs = 1 << i; secs = 1 << i;
@@ -499,7 +497,7 @@ schedule_fb_drift(struct timeval *now)
if (i <= fb_drift_max) { if (i <= fb_drift_max) {
next_fb_drift = i; next_fb_drift = i;
UTI_AddDoubleToTimeval(now, secs - unsynchronised, &when); UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL); fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i); DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i);
} }
@@ -727,7 +725,7 @@ leap_start_timeout(void *arg)
static void static void
set_leap_timeout(time_t now) set_leap_timeout(time_t now)
{ {
struct timeval when; struct timespec when;
/* Stop old timer if there is one */ /* Stop old timer if there is one */
SCH_RemoveTimeout(leap_timeout_id); SCH_RemoveTimeout(leap_timeout_id);
@@ -741,12 +739,12 @@ set_leap_timeout(time_t now)
will be corrected by the system, timeout slightly sooner to be sure it will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */ will happen before the system correction. */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600); when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_usec = 0; when.tv_nsec = 0;
if (our_leap_sec < 0) if (our_leap_sec < 0)
when.tv_sec--; when.tv_sec--;
if (leap_mode == REF_LeapModeSystem) { if (leap_mode == REF_LeapModeSystem) {
when.tv_sec--; when.tv_sec--;
when.tv_usec = 500000; when.tv_nsec = 500000000;
} }
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL); leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
@@ -804,7 +802,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
/* ================================================== */ /* ================================================== */
static void static void
write_log(struct timeval *ref_time, char *ref, int stratum, NTP_Leap leap, write_log(struct timespec *ref_time, char *ref, int stratum, NTP_Leap leap,
double freq, double skew, double offset, int combined_sources, double freq, double skew, double offset, int combined_sources,
double offset_sd, double uncorrected_offset) double offset_sd, double uncorrected_offset)
{ {
@@ -881,7 +879,7 @@ REF_SetReference(int stratum,
int combined_sources, int combined_sources,
uint32_t ref_id, uint32_t ref_id,
IPAddr *ref_ip, IPAddr *ref_ip,
struct timeval *ref_time, struct timespec *ref_time,
double offset, double offset,
double offset_sd, double offset_sd,
double frequency, double frequency,
@@ -902,7 +900,8 @@ REF_SetReference(int stratum,
double elapsed; double elapsed;
double correction_rate; double correction_rate;
double uncorrected_offset, accumulate_offset, step_offset; double uncorrected_offset, accumulate_offset, step_offset;
struct timeval now, raw_now; struct timespec now, raw_now;
NTP_int64 ref_fuzz;
assert(initialised); assert(initialised);
@@ -936,9 +935,9 @@ REF_SetReference(int stratum,
LCL_ReadRawTime(&raw_now); LCL_ReadRawTime(&raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL); LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
UTI_AddDoubleToTimeval(&raw_now, uncorrected_offset, &now); UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time); elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
our_offset = offset + elapsed * frequency; our_offset = offset + elapsed * frequency;
if (!is_offset_ok(our_offset)) if (!is_offset_ok(our_offset))
@@ -956,7 +955,7 @@ REF_SetReference(int stratum,
our_root_dispersion = root_dispersion; our_root_dispersion = root_dispersion;
if (last_ref_update.tv_sec) { if (last_ref_update.tv_sec) {
UTI_DiffTimevalsToDouble(&update_interval, &now, &last_ref_update); update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
if (update_interval < 0.0) if (update_interval < 0.0)
update_interval = 0.0; update_interval = 0.0;
} else { } else {
@@ -1043,6 +1042,15 @@ REF_SetReference(int stratum,
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion); LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
/* 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
receive timestamps in the interleaved symmetric NTP mode */
UTI_GetNtp64Fuzz(&ref_fuzz, 0);
UTI_TimespecToNtp64(&our_ref_time, &ref_fuzz, &ref_fuzz);
UTI_Ntp64ToTimespec(&ref_fuzz, &our_ref_time);
if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
our_ref_time.tv_sec--;
abs_freq_ppm = LCL_ReadAbsoluteFrequency(); abs_freq_ppm = LCL_ReadAbsoluteFrequency();
write_log(&now, write_log(&now,
@@ -1089,7 +1097,7 @@ REF_SetReference(int stratum,
void void
REF_SetManualReference REF_SetManualReference
( (
struct timeval *ref_time, struct timespec *ref_time,
double offset, double offset,
double frequency, double frequency,
double skew double skew
@@ -1108,7 +1116,7 @@ void
REF_SetUnsynchronised(void) REF_SetUnsynchronised(void)
{ {
/* Variables required for logging to statistics log */ /* Variables required for logging to statistics log */
struct timeval now, now_raw; struct timespec now, now_raw;
double uncorrected_offset; double uncorrected_offset;
assert(initialised); assert(initialised);
@@ -1121,7 +1129,7 @@ REF_SetUnsynchronised(void)
LCL_ReadRawTime(&now_raw); LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &uncorrected_offset, NULL); LCL_GetOffsetCorrection(&now_raw, &uncorrected_offset, NULL);
UTI_AddDoubleToTimeval(&now_raw, uncorrected_offset, &now); UTI_AddDoubleToTimespec(&now_raw, uncorrected_offset, &now);
if (fb_drifts) { if (fb_drifts) {
schedule_fb_drift(&now); schedule_fb_drift(&now);
@@ -1149,12 +1157,12 @@ REF_SetUnsynchronised(void)
void void
REF_GetReferenceParams REF_GetReferenceParams
( (
struct timeval *local_time, struct timespec *local_time,
int *is_synchronised, int *is_synchronised,
NTP_Leap *leap_status, NTP_Leap *leap_status,
int *stratum, int *stratum,
uint32_t *ref_id, uint32_t *ref_id,
struct timeval *ref_time, struct timespec *ref_time,
double *root_delay, double *root_delay,
double *root_dispersion double *root_dispersion
) )
@@ -1164,7 +1172,7 @@ REF_GetReferenceParams
assert(initialised); assert(initialised);
if (are_we_synchronised) { if (are_we_synchronised) {
UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time); elapsed = UTI_DiffTimespecsToDouble(local_time, &our_ref_time);
dispersion = our_root_dispersion + dispersion = our_root_dispersion +
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed; (our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
} else { } else {
@@ -1215,7 +1223,7 @@ REF_GetReferenceParams
*leap_status = LEAP_Unsynchronised; *leap_status = LEAP_Unsynchronised;
*stratum = NTP_MAX_STRATUM; *stratum = NTP_MAX_STRATUM;
*ref_id = NTP_REFID_UNSYNC; *ref_id = NTP_REFID_UNSYNC;
ref_time->tv_sec = ref_time->tv_usec = 0; UTI_ZeroTimespec(ref_time);
/* These values seem to be standard for a client, and /* These values seem to be standard for a client, and
any peer or client of ours will ignore them anyway because any peer or client of ours will ignore them anyway because
we don't claim to be synchronised */ we don't claim to be synchronised */
@@ -1230,7 +1238,7 @@ REF_GetReferenceParams
int int
REF_GetOurStratum(void) REF_GetOurStratum(void)
{ {
struct timeval now_cooked, ref_time; struct timespec now_cooked, ref_time;
int synchronised, stratum; int synchronised, stratum;
NTP_Leap leap_status; NTP_Leap leap_status;
uint32_t ref_id; uint32_t ref_id;
@@ -1303,7 +1311,7 @@ REF_DisableLocal(void)
int REF_IsLeapSecondClose(void) int REF_IsLeapSecondClose(void)
{ {
struct timeval now, now_raw; struct timespec now, now_raw;
time_t t; time_t t;
if (!our_leap_sec) if (!our_leap_sec)
@@ -1327,13 +1335,13 @@ int REF_IsLeapSecondClose(void)
void void
REF_GetTrackingReport(RPT_TrackingReport *rep) REF_GetTrackingReport(RPT_TrackingReport *rep)
{ {
struct timeval now_raw, now_cooked; struct timespec now_raw, now_cooked;
double correction; double correction;
int synchronised; int synchronised;
LCL_ReadRawTime(&now_raw); LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &correction, NULL); LCL_GetOffsetCorrection(&now_raw, &correction, NULL);
UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked); UTI_AddDoubleToTimespec(&now_raw, correction, &now_cooked);
REF_GetReferenceParams(&now_cooked, &synchronised, REF_GetReferenceParams(&now_cooked, &synchronised,
&rep->leap_status, &rep->stratum, &rep->leap_status, &rep->stratum,

View File

@@ -99,12 +99,12 @@ extern REF_LeapMode REF_GetLeapMode(void);
extern void REF_GetReferenceParams extern void REF_GetReferenceParams
( (
struct timeval *local_time, struct timespec *local_time,
int *is_synchronised, int *is_synchronised,
NTP_Leap *leap, NTP_Leap *leap,
int *stratum, int *stratum,
uint32_t *ref_id, uint32_t *ref_id,
struct timeval *ref_time, struct timespec *ref_time,
double *root_delay, double *root_delay,
double *root_dispersion double *root_dispersion
); );
@@ -140,7 +140,7 @@ extern void REF_SetReference
int combined_sources, int combined_sources,
uint32_t ref_id, uint32_t ref_id,
IPAddr *ref_ip, IPAddr *ref_ip,
struct timeval *ref_time, struct timespec *ref_time,
double offset, double offset,
double offset_sd, double offset_sd,
double frequency, double frequency,
@@ -151,7 +151,7 @@ extern void REF_SetReference
extern void REF_SetManualReference extern void REF_SetManualReference
( (
struct timeval *ref_time, struct timespec *ref_time,
double offset, double offset,
double frequency, double frequency,
double skew double skew

View File

@@ -109,7 +109,7 @@ double
RGR_GetTCoef(int dof) RGR_GetTCoef(int dof)
{ {
/* Assuming now the 99.95% quantile */ /* Assuming now the 99.95% quantile */
static double coefs[] = static const float coefs[] =
{ 636.6, 31.6, 12.92, 8.61, 6.869, { 636.6, 31.6, 12.92, 8.61, 6.869,
5.959, 5.408, 5.041, 4.781, 4.587, 5.959, 5.408, 5.041, 4.781, 4.587,
4.437, 4.318, 4.221, 4.140, 4.073, 4.437, 4.318, 4.221, 4.140, 4.073,
@@ -132,7 +132,7 @@ RGR_GetTCoef(int dof)
double double
RGR_GetChi2Coef(int dof) RGR_GetChi2Coef(int dof)
{ {
static double coefs[] = { static const float coefs[] = {
2.706, 4.605, 6.251, 7.779, 9.236, 10.645, 12.017, 13.362, 2.706, 4.605, 6.251, 7.779, 9.236, 10.645, 12.017, 13.362,
14.684, 15.987, 17.275, 18.549, 19.812, 21.064, 22.307, 23.542, 14.684, 15.987, 17.275, 18.549, 19.812, 21.064, 22.307, 23.542,
24.769, 25.989, 27.204, 28.412, 29.615, 30.813, 32.007, 33.196, 24.769, 25.989, 27.204, 28.412, 29.615, 30.813, 32.007, 33.196,
@@ -150,20 +150,6 @@ RGR_GetChi2Coef(int dof)
} }
} }
/* ================================================== */
/* Structure used for holding results of each regression */
typedef struct {
double variance;
double slope_sd;
double slope;
double offset;
double offset_sd;
double K2; /* Variance / slope_var */
int n; /* Number of points */
int dof; /* Number of degrees of freedom */
} RegressionResult;
/* ================================================== */ /* ================================================== */
/* Critical value for number of runs of residuals with same sign. /* Critical value for number of runs of residuals with same sign.
5% critical region for now. */ 5% critical region for now. */
@@ -653,3 +639,57 @@ RGR_FindBestRobustRegression
} }
/* ================================================== */ /* ================================================== */
/* This routine performs linear regression with two independent variables.
It returns non-zero status if there were enough data points and there
was a solution. */
int
RGR_MultipleRegress
(double *x1, /* first independent variable */
double *x2, /* second independent variable */
double *y, /* measured data */
int n, /* number of data points */
/* The results */
double *b2 /* estimated second slope */
/* other values are not needed yet */
)
{
double Sx1, Sx2, Sx1x1, Sx1x2, Sx2x2, Sx1y, Sx2y, Sy;
double U, V, V1, V2, V3;
int i;
if (n < 4)
return 0;
Sx1 = Sx2 = Sx1x1 = Sx1x2 = Sx2x2 = Sx1y = Sx2y = Sy = 0.0;
for (i = 0; i < n; i++) {
Sx1 += x1[i];
Sx2 += x2[i];
Sx1x1 += x1[i] * x1[i];
Sx1x2 += x1[i] * x2[i];
Sx2x2 += x2[i] * x2[i];
Sx1y += x1[i] * y[i];
Sx2y += x2[i] * y[i];
Sy += y[i];
}
U = n * (Sx1x2 * Sx1y - Sx1x1 * Sx2y) +
Sx1 * Sx1 * Sx2y - Sx1 * Sx2 * Sx1y +
Sy * (Sx2 * Sx1x1 - Sx1 * Sx1x2);
V1 = n * (Sx1x2 * Sx1x2 - Sx1x1 * Sx2x2);
V2 = Sx1 * Sx1 * Sx2x2 + Sx2 * Sx2 * Sx1x1;
V3 = -2.0 * Sx1 * Sx2 * Sx1x2;
V = V1 + V2 + V3;
/* Check if there is a (numerically stable) solution */
if (fabs(V) * 1.0e10 <= -V1 + V2 + fabs(V3))
return 0;
*b2 = U / V;
return 1;
}

View File

@@ -119,4 +119,16 @@ RGR_FindBestRobustRegression
int *n_runs, int *n_runs,
int *best_start); int *best_start);
int
RGR_MultipleRegress
(double *x1, /* first independent variable */
double *x2, /* second independent variable */
double *y, /* measured data */
int n, /* number of data points */
/* The results */
double *b2 /* estimated second slope */
);
#endif /* GOT_REGRESS_H */ #endif /* GOT_REGRESS_H */

View File

@@ -31,8 +31,6 @@
#include "addressing.h" #include "addressing.h"
#include "ntp.h" #include "ntp.h"
#define REPORT_INVALID_OFFSET 0x80000000
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
int stratum; int stratum;
@@ -53,7 +51,7 @@ typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
int stratum; int stratum;
NTP_Leap leap_status; NTP_Leap leap_status;
struct timeval ref_time; struct timespec ref_time;
double current_correction; double current_correction;
double last_offset; double last_offset;
double rms_offset; double rms_offset;
@@ -79,7 +77,7 @@ typedef struct {
} RPT_SourcestatsReport; } RPT_SourcestatsReport;
typedef struct { typedef struct {
struct timeval ref_time; struct timespec ref_time;
unsigned short n_samples; unsigned short n_samples;
unsigned short n_runs; unsigned short n_runs;
unsigned long span_seconds; unsigned long span_seconds;
@@ -109,7 +107,7 @@ typedef struct {
} RPT_ServerStatsReport; } RPT_ServerStatsReport;
typedef struct { typedef struct {
struct timeval when; struct timespec when;
double slewed_offset; double slewed_offset;
double orig_offset; double orig_offset;
double residual; double residual;
@@ -133,4 +131,33 @@ typedef struct {
double remaining_time; double remaining_time;
} RPT_SmoothingReport; } RPT_SmoothingReport;
typedef struct {
IPAddr remote_addr;
IPAddr local_addr;
uint16_t remote_port;
uint8_t leap;
uint8_t version;
uint8_t mode;
uint8_t stratum;
int8_t poll;
int8_t precision;
double root_delay;
double root_dispersion;
uint32_t ref_id;
struct timespec ref_time;
double offset;
double peer_delay;
double peer_dispersion;
double response_time;
double jitter_asymmetry;
uint16_t tests;
int interleaved;
int authenticated;
char tx_tss_char;
char rx_tss_char;
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
} RPT_NTPReport;
#endif /* GOT_REPORTS_H */ #endif /* GOT_REPORTS_H */

2
rtc.c
View File

@@ -98,7 +98,7 @@ get_driftfile_time(void)
static void static void
apply_driftfile_time(time_t t) apply_driftfile_time(time_t t)
{ {
struct timeval now; struct timespec now;
LCL_ReadCookedTime(&now, NULL); LCL_ReadCookedTime(&now, NULL);

View File

@@ -50,7 +50,7 @@
static void measurement_timeout(void *any); static void measurement_timeout(void *any);
static void read_from_device(void *any); static void read_from_device(int fd_, int event, void *any);
/* ================================================== */ /* ================================================== */
@@ -92,9 +92,8 @@ static double *rtc_trim = NULL;
static time_t rtc_ref; static time_t rtc_ref;
/* System clock (gettimeofday) samples associated with the above /* System clock samples associated with the above samples. */
samples. */ static struct timespec *system_times = NULL;
static struct timeval *system_times = NULL;
/* Number of samples currently stored. */ /* Number of samples currently stored. */
static int n_samples; static int n_samples;
@@ -170,7 +169,7 @@ discard_samples(int new_first)
memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t)); memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double)); memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timeval)); memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
n_samples = n_to_save; n_samples = n_to_save;
} }
@@ -180,7 +179,7 @@ discard_samples(int new_first)
#define NEW_FIRST_WHEN_FULL 4 #define NEW_FIRST_WHEN_FULL 4
static void static void
accumulate_sample(time_t rtc, struct timeval *sys) accumulate_sample(time_t rtc, struct timespec *sys)
{ {
if (n_samples == MAX_SAMPLES) { if (n_samples == MAX_SAMPLES) {
@@ -225,7 +224,7 @@ run_regression(int new_sample,
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref); rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref);
offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) - offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
(1.0e-6 * (double) system_times[i].tv_usec) + (1.0e-9 * system_times[i].tv_nsec) +
rtc_rel[i]); rtc_rel[i]);
} }
@@ -262,7 +261,7 @@ run_regression(int new_sample,
static void static void
slew_samples slew_samples
(struct timeval *raw, struct timeval *cooked, (struct timespec *raw, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -278,7 +277,7 @@ slew_samples
} }
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
UTI_AdjustTimeval(system_times + i, cooked, system_times + i, &delta_time, UTI_AdjustTimespec(system_times + i, cooked, system_times + i, &delta_time,
dfreq, doffset); dfreq, doffset);
} }
@@ -534,7 +533,7 @@ RTC_Linux_Initialise(void)
{ {
rtc_sec = MallocArray(time_t, MAX_SAMPLES); rtc_sec = MallocArray(time_t, MAX_SAMPLES);
rtc_trim = MallocArray(double, MAX_SAMPLES); rtc_trim = MallocArray(double, MAX_SAMPLES);
system_times = MallocArray(struct timeval, MAX_SAMPLES); system_times = MallocArray(struct timespec, MAX_SAMPLES);
/* Setup details depending on configuration options */ /* Setup details depending on configuration options */
setup_config(); setup_config();
@@ -564,7 +563,7 @@ RTC_Linux_Initialise(void)
operating_mode = OM_NORMAL; operating_mode = OM_NORMAL;
/* Register file handler */ /* Register file handler */
SCH_AddInputFileHandler(fd, read_from_device, NULL); SCH_AddFileHandler(fd, SCH_FILE_INPUT, read_from_device, NULL);
/* Register slew handler */ /* Register slew handler */
LCL_AddParameterChangeHandler(slew_samples, NULL); LCL_AddParameterChangeHandler(slew_samples, NULL);
@@ -585,7 +584,7 @@ RTC_Linux_Finalise(void)
/* Remove input file handler */ /* Remove input file handler */
if (fd >= 0) { if (fd >= 0) {
SCH_RemoveInputFileHandler(fd); SCH_RemoveFileHandler(fd);
close(fd); close(fd);
/* Save the RTC data */ /* Save the RTC data */
@@ -758,7 +757,7 @@ maybe_autotrim(void)
/* ================================================== */ /* ================================================== */
static void static void
process_reading(time_t rtc_time, struct timeval *system_time) process_reading(time_t rtc_time, struct timespec *system_time)
{ {
double rtc_fast; double rtc_fast;
@@ -791,7 +790,7 @@ process_reading(time_t rtc_time, struct timeval *system_time)
if (logfileid != -1) { if (logfileid != -1) {
rtc_fast = (double)(rtc_time - system_time->tv_sec) - 1.0e-6 * (double) system_time->tv_usec; rtc_fast = (rtc_time - system_time->tv_sec) - 1.0e-9 * system_time->tv_nsec;
LOG_FileWrite(logfileid, "%s %14.6f %1d %14.6f %12.3f %2d %2d %4d", LOG_FileWrite(logfileid, "%s %14.6f %1d %14.6f %12.3f %2d %2d %4d",
UTI_TimeToLogForm(system_time->tv_sec), UTI_TimeToLogForm(system_time->tv_sec),
@@ -805,11 +804,11 @@ process_reading(time_t rtc_time, struct timeval *system_time)
/* ================================================== */ /* ================================================== */
static void static void
read_from_device(void *any) read_from_device(int fd_, int event, void *any)
{ {
int status; int status;
unsigned long data; unsigned long data;
struct timeval sys_time; struct timespec sys_time;
struct rtc_time rtc_raw; struct rtc_time rtc_raw;
struct tm rtc_tm; struct tm rtc_tm;
time_t rtc_t; time_t rtc_t;
@@ -821,7 +820,7 @@ read_from_device(void *any)
/* This looks like a bad error : the file descriptor was indicating it was /* This looks like a bad error : the file descriptor was indicating it was
* ready to read but we couldn't read anything. Give up. */ * 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, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
SCH_RemoveInputFileHandler(fd); SCH_RemoveFileHandler(fd);
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */ switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
close(fd); close(fd);
fd = -1; fd = -1;
@@ -849,7 +848,7 @@ read_from_device(void *any)
goto turn_off_interrupt; goto turn_off_interrupt;
} }
/* Convert RTC time into a struct timeval */ /* Convert RTC time into a struct timespec */
rtc_tm.tm_sec = rtc_raw.tm_sec; rtc_tm.tm_sec = rtc_raw.tm_sec;
rtc_tm.tm_min = rtc_raw.tm_min; rtc_tm.tm_min = rtc_raw.tm_min;
rtc_tm.tm_hour = rtc_raw.tm_hour; rtc_tm.tm_hour = rtc_raw.tm_hour;
@@ -978,7 +977,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
struct tm rtc_tm; struct tm rtc_tm;
time_t rtc_t; time_t rtc_t;
double accumulated_error, sys_offset; double accumulated_error, sys_offset;
struct timeval new_sys_time, old_sys_time; struct timespec new_sys_time, old_sys_time;
coefs_file_name = CNF_GetRtcFile(); coefs_file_name = CNF_GetRtcFile();
@@ -1032,16 +1031,16 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
new_sys_time.tv_sec = rtc_t; new_sys_time.tv_sec = rtc_t;
/* Average error in the RTC reading */ /* Average error in the RTC reading */
new_sys_time.tv_usec = 500000; new_sys_time.tv_nsec = 500000000;
UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time); UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) { if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)"); LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
return 0; return 0;
} }
UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time); sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */ /* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) { if (fabs(sys_offset) >= 1.0) {
@@ -1064,7 +1063,7 @@ int
RTC_Linux_GetReport(RPT_RTC_Report *report) RTC_Linux_GetReport(RPT_RTC_Report *report)
{ {
report->ref_time.tv_sec = coef_ref_time; report->ref_time.tv_sec = coef_ref_time;
report->ref_time.tv_usec = 0; report->ref_time.tv_nsec = 0;
report->n_samples = n_samples; report->n_samples = n_samples;
report->n_runs = n_runs; report->n_runs = n_runs;
if (n_samples > 1) { if (n_samples > 1) {
@@ -1083,8 +1082,7 @@ RTC_Linux_GetReport(RPT_RTC_Report *report)
int int
RTC_Linux_Trim(void) RTC_Linux_Trim(void)
{ {
struct timeval now; struct timespec now;
/* Remember the slope coefficient - we won't be able to determine a /* Remember the slope coefficient - we won't be able to determine a
good one in a few seconds when we determine the new offset! */ good one in a few seconds when we determine the new offset! */
@@ -1114,7 +1112,7 @@ RTC_Linux_Trim(void)
/* Estimate the offset in case writertc is called or chronyd /* Estimate the offset in case writertc is called or chronyd
is terminated during rapid sampling */ is terminated during rapid sampling */
coef_seconds_fast = -now.tv_usec / 1e6 + 0.5; coef_seconds_fast = -now.tv_nsec / 1.0e9 + 0.5;
coef_ref_time = now.tv_sec; coef_ref_time = now.tv_sec;
/* And start rapid sampling, interrupts on now */ /* And start rapid sampling, interrupts on now */

328
sched.c
View File

@@ -44,17 +44,6 @@ static int initialised = 0;
/* ================================================== */ /* ================================================== */
/* Variables to handle the capability to dispatch on particular file
handles becoming readable */
/* Each bit set in this fd set corresponds to a read descriptor that
we are watching and with which we have a handler associated in the
file_handlers array */
static fd_set read_fds;
/* This is the number of bits that we have set in read_fds */
static unsigned int n_read_fds;
/* One more than the highest file descriptor that is registered */ /* One more than the highest file descriptor that is registered */
static unsigned int one_highest_fd; static unsigned int one_highest_fd;
@@ -67,12 +56,13 @@ static unsigned int one_highest_fd;
typedef struct { typedef struct {
SCH_FileHandler handler; SCH_FileHandler handler;
SCH_ArbitraryArgument arg; SCH_ArbitraryArgument arg;
int events;
} FileHandlerEntry; } FileHandlerEntry;
static ARR_Instance file_handlers; static ARR_Instance file_handlers;
/* Timestamp when last select() returned */ /* Timestamp when last select() returned */
static struct timeval last_select_ts, last_select_ts_raw; static struct timespec last_select_ts, last_select_ts_raw;
static double last_select_ts_err; static double last_select_ts_err;
/* ================================================== */ /* ================================================== */
@@ -83,7 +73,7 @@ typedef struct _TimerQueueEntry
{ {
struct _TimerQueueEntry *next; /* Forward and back links in the list */ struct _TimerQueueEntry *next; /* Forward and back links in the list */
struct _TimerQueueEntry *prev; struct _TimerQueueEntry *prev;
struct timeval tv; /* Local system time at which the struct timespec ts; /* Local system time at which the
timeout is to expire. Clearly this timeout is to expire. Clearly this
must be in terms of what the must be in terms of what the
operating system thinks of as operating system thinks of as
@@ -111,7 +101,7 @@ static SCH_TimeoutID next_tqe_id;
static TimerQueueEntry *tqe_free_list = NULL; static TimerQueueEntry *tqe_free_list = NULL;
/* Timestamp when was last timeout dispatched for each class */ /* Timestamp when was last timeout dispatched for each class */
static struct timeval last_class_dispatch[SCH_NumberOfClasses]; static struct timespec last_class_dispatch[SCH_NumberOfClasses];
/* ================================================== */ /* ================================================== */
@@ -120,8 +110,8 @@ static int need_to_exit;
/* ================================================== */ /* ================================================== */
static void static void
handle_slew(struct timeval *raw, handle_slew(struct timespec *raw,
struct timeval *cooked, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -132,9 +122,6 @@ handle_slew(struct timeval *raw,
void void
SCH_Initialise(void) SCH_Initialise(void)
{ {
FD_ZERO(&read_fds);
n_read_fds = 0;
file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry)); file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry));
n_timer_queue_entries = 0; n_timer_queue_entries = 0;
@@ -166,73 +153,85 @@ SCH_Finalise(void) {
/* ================================================== */ /* ================================================== */
void void
SCH_AddInputFileHandler SCH_AddFileHandler
(int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg) (int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
{
FileHandlerEntry *ptr;
assert(initialised);
assert(events);
assert(fd >= 0);
if (fd >= FD_SETSIZE)
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
/* Resize the array if the descriptor is highest so far */
while (ARR_GetSize(file_handlers) <= fd) {
ptr = ARR_GetNewElement(file_handlers);
ptr->handler = NULL;
ptr->arg = NULL;
ptr->events = 0;
}
ptr = ARR_GetElement(file_handlers, fd);
/* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests
a bug somewhere else in the program. */
assert(!ptr->handler);
ptr->handler = handler;
ptr->arg = arg;
ptr->events = events;
if (one_highest_fd < fd + 1)
one_highest_fd = fd + 1;
}
/* ================================================== */
void
SCH_RemoveFileHandler(int fd)
{ {
FileHandlerEntry *ptr; FileHandlerEntry *ptr;
assert(initialised); assert(initialised);
if (fd >= FD_SETSIZE) ptr = ARR_GetElement(file_handlers, fd);
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
/* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests
a bug somewhere else in the program. */
if (FD_ISSET(fd, &read_fds))
assert(0);
++n_read_fds;
if (ARR_GetSize(file_handlers) < fd + 1)
ARR_SetSize(file_handlers, fd + 1);
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
ptr->handler = handler;
ptr->arg = arg;
FD_SET(fd, &read_fds);
if ((fd + 1) > one_highest_fd) {
one_highest_fd = fd + 1;
}
}
/* ================================================== */
void
SCH_RemoveInputFileHandler(int fd)
{
int fds_left, fd_to_check;
assert(initialised);
/* Check that a handler was registered for the fd in question */ /* Check that a handler was registered for the fd in question */
if (!FD_ISSET(fd, &read_fds)) assert(ptr->handler);
assert(0);
--n_read_fds; ptr->handler = NULL;
ptr->arg = NULL;
FD_CLR(fd, &read_fds); ptr->events = 0;
/* Find new highest file descriptor */ /* Find new highest file descriptor */
fds_left = n_read_fds; while (one_highest_fd > 0) {
fd_to_check = 0; ptr = ARR_GetElement(file_handlers, one_highest_fd - 1);
while (fds_left > 0) { if (ptr->handler)
if (FD_ISSET(fd_to_check, &read_fds)) { break;
--fds_left; one_highest_fd--;
}
++fd_to_check;
} }
one_highest_fd = fd_to_check;
} }
/* ================================================== */ /* ================================================== */
void void
SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw) SCH_SetFileHandlerEvents(int fd, int events)
{
FileHandlerEntry *ptr;
assert(events);
ptr = ARR_GetElement(file_handlers, fd);
ptr->events = events;
}
/* ================================================== */
void
SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw)
{ {
if (cooked) { if (cooked) {
*cooked = last_select_ts; *cooked = last_select_ts;
@@ -299,7 +298,7 @@ try_again:
/* ================================================== */ /* ================================================== */
SCH_TimeoutID SCH_TimeoutID
SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg) SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{ {
TimerQueueEntry *new_tqe; TimerQueueEntry *new_tqe;
TimerQueueEntry *ptr; TimerQueueEntry *ptr;
@@ -311,12 +310,12 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
new_tqe->id = get_new_tqe_id(); new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler; new_tqe->handler = handler;
new_tqe->arg = arg; new_tqe->arg = arg;
new_tqe->tv = *tv; new_tqe->ts = *ts;
new_tqe->class = SCH_ReservedTimeoutValue; new_tqe->class = SCH_ReservedTimeoutValue;
/* Now work out where to insert the new entry in the list */ /* Now work out where to insert the new entry in the list */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) { for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
if (UTI_CompareTimevals(&new_tqe->tv, &ptr->tv) == -1) { if (UTI_CompareTimespecs(&new_tqe->ts, &ptr->ts) == -1) {
/* If the new entry comes before the current pointer location in /* If the new entry comes before the current pointer location in
the list, we want to insert the new entry just before ptr. */ the list, we want to insert the new entry just before ptr. */
break; break;
@@ -343,14 +342,14 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
SCH_TimeoutID SCH_TimeoutID
SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg) SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{ {
struct timeval now, then; struct timespec now, then;
assert(initialised); assert(initialised);
assert(delay >= 0.0); assert(delay >= 0.0);
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
UTI_AddDoubleToTimeval(&now, delay, &then); UTI_AddDoubleToTimespec(&now, delay, &then);
if (UTI_CompareTimevals(&now, &then) > 0) { if (UTI_CompareTimespecs(&now, &then) > 0) {
LOG_FATAL(LOGF_Scheduler, "Timeout overflow"); LOG_FATAL(LOGF_Scheduler, "Timeout overflow");
} }
@@ -367,7 +366,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
{ {
TimerQueueEntry *new_tqe; TimerQueueEntry *new_tqe;
TimerQueueEntry *ptr; TimerQueueEntry *ptr;
struct timeval now; struct timespec now;
double diff, r; double diff, r;
double new_min_delay; double new_min_delay;
@@ -376,10 +375,10 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
assert(class < SCH_NumberOfClasses); assert(class < SCH_NumberOfClasses);
if (randomness > 0.0) { if (randomness > 0.0) {
uint16_t rnd; uint32_t rnd;
UTI_GetRandomBytes(&rnd, sizeof (rnd)); UTI_GetRandomBytes(&rnd, sizeof (rnd));
r = rnd / (double)0xffff * randomness + 1.0; r = rnd * (randomness / (uint32_t)-1) + 1.0;
min_delay *= r; min_delay *= r;
separation *= r; separation *= r;
} }
@@ -388,7 +387,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
new_min_delay = min_delay; new_min_delay = min_delay;
/* Check the separation from the last dispatched timeout */ /* Check the separation from the last dispatched timeout */
UTI_DiffTimevalsToDouble(&diff, &now, &last_class_dispatch[class]); diff = UTI_DiffTimespecsToDouble(&now, &last_class_dispatch[class]);
if (diff < separation && diff >= 0.0 && diff + new_min_delay < separation) { if (diff < separation && diff >= 0.0 && diff + new_min_delay < separation) {
new_min_delay = separation - diff; new_min_delay = separation - diff;
} }
@@ -397,7 +396,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
if necessary to keep at least the separation away */ if necessary to keep at least the separation away */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) { for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
if (ptr->class == class) { if (ptr->class == class) {
UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now); diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
if (new_min_delay > diff) { if (new_min_delay > diff) {
if (new_min_delay - diff < separation) { if (new_min_delay - diff < separation) {
new_min_delay = diff + separation; new_min_delay = diff + separation;
@@ -411,7 +410,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
} }
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) { for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now); diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
if (diff > new_min_delay) { if (diff > new_min_delay) {
break; break;
} }
@@ -423,7 +422,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
new_tqe->id = get_new_tqe_id(); new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler; new_tqe->handler = handler;
new_tqe->arg = arg; new_tqe->arg = arg;
UTI_AddDoubleToTimeval(&now, new_min_delay, &new_tqe->tv); UTI_AddDoubleToTimespec(&now, new_min_delay, &new_tqe->ts);
new_tqe->class = class; new_tqe->class = class;
new_tqe->next = ptr; new_tqe->next = ptr;
@@ -477,7 +476,7 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
completed). */ completed). */
static void static void
dispatch_timeouts(struct timeval *now) { dispatch_timeouts(struct timespec *now) {
TimerQueueEntry *ptr; TimerQueueEntry *ptr;
SCH_TimeoutHandler handler; SCH_TimeoutHandler handler;
SCH_ArbitraryArgument arg; SCH_ArbitraryArgument arg;
@@ -487,7 +486,7 @@ dispatch_timeouts(struct timeval *now) {
LCL_ReadRawTime(now); LCL_ReadRawTime(now);
if (!(n_timer_queue_entries > 0 && if (!(n_timer_queue_entries > 0 &&
UTI_CompareTimevals(now, &(timer_queue.next->tv)) >= 0)) { UTI_CompareTimespecs(now, &timer_queue.next->ts) >= 0)) {
break; break;
} }
@@ -520,35 +519,49 @@ dispatch_timeouts(struct timeval *now) {
/* ================================================== */ /* ================================================== */
/* nfh is the number of bits set in fhs */ /* nfd is the number of bits set in all fd_sets */
static void static void
dispatch_filehandlers(int nfh, fd_set *fhs) dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
{ {
FileHandlerEntry *ptr; FileHandlerEntry *ptr;
int fh = 0; int fd;
while (nfh > 0) { for (fd = 0; nfd && fd < one_highest_fd; fd++) {
if (FD_ISSET(fh, fhs)) { 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);
nfd--;
/* This descriptor can be read from, dispatch its handler */ /* Don't try to read from it now */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fh); if (read_fds && FD_ISSET(fd, read_fds)) {
(ptr->handler)(ptr->arg); FD_CLR(fd, read_fds);
nfd--;
/* Decrement number of readable files still to find */ }
--nfh;
} }
++fh; 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);
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);
nfd--;
}
}
} }
/* ================================================== */ /* ================================================== */
static void static void
handle_slew(struct timeval *raw, handle_slew(struct timespec *raw,
struct timeval *cooked, struct timespec *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -566,17 +579,69 @@ handle_slew(struct timeval *raw,
/* If a step change occurs, just shift all raw time stamps by the offset */ /* If a step change occurs, just shift all raw time stamps by the offset */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) { for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
UTI_AddDoubleToTimeval(&ptr->tv, -doffset, &ptr->tv); UTI_AddDoubleToTimespec(&ptr->ts, -doffset, &ptr->ts);
} }
for (i = 0; i < SCH_NumberOfClasses; i++) { for (i = 0; i < SCH_NumberOfClasses; i++) {
UTI_AddDoubleToTimeval(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]); UTI_AddDoubleToTimespec(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]);
} }
UTI_AddDoubleToTimeval(&last_select_ts_raw, -doffset, &last_select_ts_raw); UTI_AddDoubleToTimespec(&last_select_ts_raw, -doffset, &last_select_ts_raw);
} }
UTI_AdjustTimeval(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset); UTI_AdjustTimespec(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset);
}
/* ================================================== */
static void
fill_fd_sets(fd_set **read_fds, fd_set **write_fds, fd_set **except_fds)
{
FileHandlerEntry *handlers;
fd_set *rd, *wr, *ex;
int i, n, events;
n = ARR_GetSize(file_handlers);
handlers = ARR_GetElements(file_handlers);
rd = wr = ex = NULL;
for (i = 0; i < n; i++) {
events = handlers[i].events;
if (!events)
continue;
if (events & SCH_FILE_INPUT) {
if (!rd) {
rd = *read_fds;
FD_ZERO(rd);
}
FD_SET(i, rd);
}
if (events & SCH_FILE_OUTPUT) {
if (!wr) {
wr = *write_fds;
FD_ZERO(wr);
}
FD_SET(i, wr);
}
if (events & SCH_FILE_EXCEPTION) {
if (!ex) {
ex = *except_fds;
FD_ZERO(ex);
}
FD_SET(i, ex);
}
}
if (!rd)
*read_fds = NULL;
if (!wr)
*write_fds = NULL;
if (!ex)
*except_fds = NULL;
} }
/* ================================================== */ /* ================================================== */
@@ -584,31 +649,33 @@ handle_slew(struct timeval *raw,
#define JUMP_DETECT_THRESHOLD 10 #define JUMP_DETECT_THRESHOLD 10
static int static int
check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout, check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
struct timeval *orig_select_tv, struct timeval *orig_select_tv,
struct timeval *rem_select_tv) struct timeval *rem_select_tv)
{ {
struct timeval elapsed_min, elapsed_max; struct timespec elapsed_min, elapsed_max, orig_select_ts, rem_select_ts;
double step, elapsed; double step, elapsed;
UTI_TimevalToTimespec(orig_select_tv, &orig_select_ts);
/* Get an estimate of the time spent waiting in the select() call. On some /* Get an estimate of the time spent waiting in the select() call. On some
systems (e.g. Linux) the timeout timeval is modified to return the systems (e.g. Linux) the timeout timeval is modified to return the
remaining time, use that information. */ remaining time, use that information. */
if (timeout) { if (timeout) {
elapsed_max = elapsed_min = *orig_select_tv; elapsed_max = elapsed_min = orig_select_ts;
} else if (rem_select_tv && rem_select_tv->tv_sec >= 0 && } else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
rem_select_tv->tv_sec <= orig_select_tv->tv_sec && rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
(rem_select_tv->tv_sec != orig_select_tv->tv_sec || (rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
rem_select_tv->tv_usec != orig_select_tv->tv_usec)) { rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
UTI_DiffTimevals(&elapsed_min, orig_select_tv, rem_select_tv); UTI_TimevalToTimespec(rem_select_tv, &rem_select_ts);
UTI_DiffTimespecs(&elapsed_min, &orig_select_ts, &rem_select_ts);
elapsed_max = elapsed_min; elapsed_max = elapsed_min;
} else { } else {
if (rem_select_tv) if (rem_select_tv)
elapsed_max = *orig_select_tv; elapsed_max = orig_select_ts;
else else
UTI_DiffTimevals(&elapsed_max, raw, prev_raw); UTI_DiffTimespecs(&elapsed_max, raw, prev_raw);
elapsed_min.tv_sec = 0; UTI_ZeroTimespec(&elapsed_min);
elapsed_min.tv_usec = 0;
} }
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec > if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
@@ -621,8 +688,8 @@ check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
return 1; return 1;
} }
UTI_DiffTimevalsToDouble(&step, &last_select_ts_raw, raw); step = UTI_DiffTimespecsToDouble(&last_select_ts_raw, raw);
UTI_TimevalToDouble(&elapsed_min, &elapsed); elapsed = UTI_TimespecToDouble(&elapsed_min);
step += elapsed; step += elapsed;
/* Cooked time may no longer be valid after dispatching the handlers */ /* Cooked time may no longer be valid after dispatching the handlers */
@@ -636,10 +703,11 @@ check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
void void
SCH_MainLoop(void) SCH_MainLoop(void)
{ {
fd_set rd; fd_set read_fds, write_fds, except_fds;
fd_set *p_read_fds, *p_write_fds, *p_except_fds;
int status, errsv; int status, errsv;
struct timeval tv, saved_tv, *ptv; struct timeval tv, saved_tv, *ptv;
struct timeval now, saved_now, cooked; struct timespec ts, now, saved_now, cooked;
double err; double err;
assert(initialised); assert(initialised);
@@ -655,28 +723,28 @@ SCH_MainLoop(void)
/* Check whether there is a timeout and set it up */ /* Check whether there is a timeout and set it up */
if (n_timer_queue_entries > 0) { if (n_timer_queue_entries > 0) {
UTI_DiffTimespecs(&ts, &timer_queue.next->ts, &now);
assert(ts.tv_sec > 0 || ts.tv_nsec > 0);
UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now); UTI_TimespecToTimeval(&ts, &tv);
ptv = &tv; ptv = &tv;
assert(tv.tv_sec > 0 || tv.tv_usec > 0);
saved_tv = tv; saved_tv = tv;
} else { } else {
ptv = NULL; ptv = NULL;
/* This is needed to fix a compiler warning */ saved_tv.tv_sec = saved_tv.tv_usec = 0;
saved_tv.tv_sec = 0;
} }
p_read_fds = &read_fds;
p_write_fds = &write_fds;
p_except_fds = &except_fds;
fill_fd_sets(&p_read_fds, &p_write_fds, &p_except_fds);
/* if there are no file descriptors being waited on and no /* if there are no file descriptors being waited on and no
timeout set, this is clearly ridiculous, so stop the run */ timeout set, this is clearly ridiculous, so stop the run */
if (!ptv && !n_read_fds) { if (!ptv && !p_read_fds && !p_write_fds)
LOG_FATAL(LOGF_Scheduler, "Nothing to do"); LOG_FATAL(LOGF_Scheduler, "Nothing to do");
}
/* Copy current set of read file descriptors */ status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
status = select(one_highest_fd, &rd, NULL, NULL, ptv);
errsv = errno; errsv = errno;
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
@@ -697,10 +765,8 @@ SCH_MainLoop(void)
LOG_FATAL(LOGF_Scheduler, "select() failed : %s", strerror(errsv)); LOG_FATAL(LOGF_Scheduler, "select() failed : %s", strerror(errsv));
} }
} else if (status > 0) { } else if (status > 0) {
/* A file descriptor is ready to read */ /* A file descriptor is ready for input or output */
dispatch_filehandlers(status, p_read_fds, p_write_fds, p_except_fds);
dispatch_filehandlers(status, &rd);
} else { } else {
/* No descriptors readable, timeout must have elapsed. /* No descriptors readable, timeout must have elapsed.
Therefore, tv must be non-null */ Therefore, tv must be non-null */

20
sched.h
View File

@@ -40,7 +40,7 @@ typedef enum {
} SCH_TimeoutClass; } SCH_TimeoutClass;
typedef void* SCH_ArbitraryArgument; typedef void* SCH_ArbitraryArgument;
typedef void (*SCH_FileHandler)(SCH_ArbitraryArgument); typedef void (*SCH_FileHandler)(int fd, int event, SCH_ArbitraryArgument);
typedef void (*SCH_TimeoutHandler)(SCH_ArbitraryArgument); typedef void (*SCH_TimeoutHandler)(SCH_ArbitraryArgument);
/* Exported functions */ /* Exported functions */
@@ -51,19 +51,21 @@ extern void SCH_Initialise(void);
/* Finalisation function for the module */ /* Finalisation function for the module */
extern void SCH_Finalise(void); extern void SCH_Finalise(void);
/* File events */
#define SCH_FILE_INPUT 1
#define SCH_FILE_OUTPUT 2
#define SCH_FILE_EXCEPTION 4
/* Register a handler for when select goes true on a file descriptor */ /* Register a handler for when select goes true on a file descriptor */
extern void SCH_AddInputFileHandler extern void SCH_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
(int fd, /* The file descriptor */ extern void SCH_RemoveFileHandler(int fd);
SCH_FileHandler, /* The handler routine */ extern void SCH_SetFileHandlerEvents(int fd, int events);
SCH_ArbitraryArgument /* An arbitrary passthrough argument to the handler */
);
extern void SCH_RemoveInputFileHandler(int fd);
/* Get the time stamp taken after a file descriptor became ready or a timeout expired */ /* Get the time stamp taken after a file descriptor became ready or a timeout expired */
extern void SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw); extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);
/* This queues a timeout to elapse at a given (raw) local time */ /* This queues a timeout to elapse at a given (raw) local time */
extern SCH_TimeoutID SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler, SCH_ArbitraryArgument); extern SCH_TimeoutID SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg);
/* This queues a timeout to elapse at a given delta time relative to the current (raw) time */ /* This queues a timeout to elapse at a given delta time relative to the current (raw) time */
extern SCH_TimeoutID SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler, SCH_ArbitraryArgument); extern SCH_TimeoutID SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler, SCH_ArbitraryArgument);

View File

@@ -93,17 +93,17 @@ static double max_freq;
/* Frequency offset, time offset and the time of the last smoothing update */ /* Frequency offset, time offset and the time of the last smoothing update */
static double smooth_freq; static double smooth_freq;
static double smooth_offset; static double smooth_offset;
static struct timeval last_update; static struct timespec last_update;
static void static void
get_smoothing(struct timeval *now, double *poffset, double *pfreq, get_smoothing(struct timespec *now, double *poffset, double *pfreq,
double *pwander) double *pwander)
{ {
double elapsed, length, offset, freq, wander; double elapsed, length, offset, freq, wander;
int i; int i;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update); elapsed = UTI_DiffTimespecsToDouble(now, &last_update);
offset = smooth_offset; offset = smooth_offset;
freq = smooth_freq; freq = smooth_freq;
@@ -137,7 +137,7 @@ get_smoothing(struct timeval *now, double *poffset, double *pfreq,
static void static void
update_stages(void) update_stages(void)
{ {
double s1, s2, s, l1, l2, l3, lc, f, f2; double s1, s2, s, l1, l2, l3, lc, f, f2, l1t[2], l3t[2], err[2];
int i, dir; int i, dir;
/* Prepare the three stages so that the integral of the frequency offset /* Prepare the three stages so that the integral of the frequency offset
@@ -146,22 +146,41 @@ update_stages(void)
s1 = smooth_offset / max_wander; s1 = smooth_offset / max_wander;
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander); s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
l1 = l2 = l3 = 0.0;
/* Calculate the lengths of the 1st and 3rd stage assuming there is no /* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. If length of the 1st stage comes out negative, switch frequency limit. The direction of the 1st stage is selected so that
its direction. */ the lengths will not be negative. With extremely small offsets both
for (dir = -1; dir <= 1; dir += 2) { directions may give a negative length due to numerical errors, so select
the one which gives a smaller error. */
for (i = 0, dir = -1; i <= 1; i++, dir += 2) {
err[i] = 0.0;
s = dir * s1 + s2; s = dir * s1 + s2;
if (s >= 0.0) {
l3 = sqrt(s); if (s < 0.0) {
l1 = l3 - dir * smooth_freq / max_wander; err[i] += -s;
if (l1 >= 0.0) s = 0.0;
break; }
l3t[i] = sqrt(s);
l1t[i] = l3t[i] - dir * smooth_freq / max_wander;
if (l1t[i] < 0.0) {
err[i] += l1t[i] * l1t[i];
l1t[i] = 0.0;
} }
} }
assert(dir <= 1 && l1 >= 0.0 && l3 >= 0.0); if (err[0] < err[1]) {
l1 = l1t[0];
l3 = l3t[0];
dir = -1;
} else {
l1 = l1t[1];
l3 = l3t[1];
dir = 1;
}
l2 = 0.0;
/* If the limit was reached, shorten 1st+3rd stages and set a 2nd stage */ /* If the limit was reached, shorten 1st+3rd stages and set a 2nd stage */
f = dir * smooth_freq + l1 * max_wander - max_freq; f = dir * smooth_freq + l1 * max_wander - max_freq;
@@ -195,7 +214,7 @@ update_stages(void)
} }
static void static void
update_smoothing(struct timeval *now, double offset, double freq) update_smoothing(struct timespec *now, double offset, double freq)
{ {
/* Don't accept offset/frequency until the clock has stabilized */ /* Don't accept offset/frequency until the clock has stabilized */
if (locked) { if (locked) {
@@ -215,7 +234,7 @@ update_smoothing(struct timeval *now, double offset, double freq)
} }
static void static void
handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq, handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything) double doffset, LCL_ChangeType change_type, void *anything)
{ {
double delta; double delta;
@@ -227,7 +246,7 @@ handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
update_smoothing(cooked, doffset, dfreq); update_smoothing(cooked, doffset, dfreq);
} }
UTI_AdjustTimeval(&last_update, cooked, &last_update, &delta, dfreq, doffset); UTI_AdjustTimespec(&last_update, cooked, &last_update, &delta, dfreq, doffset);
} }
void SMT_Initialise(void) void SMT_Initialise(void)
@@ -258,7 +277,7 @@ int SMT_IsEnabled(void)
} }
double double
SMT_GetOffset(struct timeval *now) SMT_GetOffset(struct timespec *now)
{ {
double offset, freq; double offset, freq;
@@ -271,7 +290,7 @@ SMT_GetOffset(struct timeval *now)
} }
void void
SMT_Activate(struct timeval *now) SMT_Activate(struct timespec *now)
{ {
if (!enabled || !locked) if (!enabled || !locked)
return; return;
@@ -283,7 +302,7 @@ SMT_Activate(struct timeval *now)
} }
void void
SMT_Reset(struct timeval *now) SMT_Reset(struct timespec *now)
{ {
int i; int i;
@@ -299,7 +318,7 @@ SMT_Reset(struct timeval *now)
} }
void void
SMT_Leap(struct timeval *now, int leap) SMT_Leap(struct timespec *now, int leap)
{ {
/* When the leap-only mode is disabled, the leap second will be accumulated /* When the leap-only mode is disabled, the leap second will be accumulated
in handle_slew() as a normal offset */ in handle_slew() as a normal offset */
@@ -310,7 +329,7 @@ SMT_Leap(struct timeval *now, int leap)
} }
int int
SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now) SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timespec *now)
{ {
double length, elapsed; double length, elapsed;
int i; int i;
@@ -327,7 +346,7 @@ SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now)
report->freq_ppm *= -1.0e6; report->freq_ppm *= -1.0e6;
report->wander_ppm *= -1.0e6; report->wander_ppm *= -1.0e6;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update); elapsed = UTI_DiffTimespecsToDouble(now, &last_update);
if (!locked && elapsed >= 0.0) { if (!locked && elapsed >= 0.0) {
for (i = 0, length = 0.0; i < NUM_STAGES; i++) for (i = 0, length = 0.0; i < NUM_STAGES; i++)
length += stages[i].length; length += stages[i].length;

View File

@@ -35,14 +35,14 @@ extern void SMT_Finalise(void);
extern int SMT_IsEnabled(void); extern int SMT_IsEnabled(void);
extern double SMT_GetOffset(struct timeval *now); extern double SMT_GetOffset(struct timespec *now);
extern void SMT_Activate(struct timeval *now); extern void SMT_Activate(struct timespec *now);
extern void SMT_Reset(struct timeval *now); extern void SMT_Reset(struct timespec *now);
extern void SMT_Leap(struct timeval *now, int leap); extern void SMT_Leap(struct timespec *now, int leap);
extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now); extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timespec *now);
#endif #endif

267
sources.c
View File

@@ -56,7 +56,7 @@ static int initialised = 0;
struct SelectInfo { struct SelectInfo {
int stratum; int stratum;
int select_ok; int select_ok;
double variance; double std_dev;
double root_distance; double root_distance;
double lo_limit; double lo_limit;
double hi_limit; double hi_limit;
@@ -71,11 +71,12 @@ typedef enum {
SRC_UNSELECTABLE, /* Has noselect option set */ SRC_UNSELECTABLE, /* Has noselect option set */
SRC_BAD_STATS, /* Doesn't have valid stats data */ SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */ SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */ SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
SRC_STALE, /* Has older samples than others */ SRC_STALE, /* Has older samples than others */
SRC_ORPHAN, /* Has stratum equal or larger than orphan stratum */ SRC_ORPHAN, /* Has stratum equal or larger than orphan stratum */
SRC_UNTRUSTED, /* Overlaps trusted sources */
SRC_FALSETICKER, /* Doesn't agree with others */ SRC_FALSETICKER, /* Doesn't agree with others */
SRC_JITTERY, /* Scatter worse than other's dispersion (not used) */
SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */ SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */
SRC_NONPREFERRED, /* Others have prefer option */ SRC_NONPREFERRED, /* Others have prefer option */
SRC_WAITS_UPDATE, /* No updates, selection postponed */ SRC_WAITS_UPDATE, /* No updates, selection postponed */
@@ -158,6 +159,7 @@ static int selected_source_index; /* Which source index is currently
#define DISTANT_PENALTY 32 #define DISTANT_PENALTY 32
static double max_distance; static double max_distance;
static double max_jitter;
static double reselect_distance; static double reselect_distance;
static double stratum_weight; static double stratum_weight;
static double combine_limit; static double combine_limit;
@@ -166,7 +168,7 @@ static double combine_limit;
/* Forward prototype */ /* Forward prototype */
static void static void
slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything); double doffset, LCL_ChangeType change_type, void *anything);
static void static void
add_dispersion(double dispersion, void *anything); add_dispersion(double dispersion, void *anything);
@@ -183,6 +185,7 @@ void SRC_Initialise(void) {
max_n_sources = 0; max_n_sources = 0;
selected_source_index = INVALID_SOURCE; selected_source_index = INVALID_SOURCE;
max_distance = CNF_GetMaxDistance(); max_distance = CNF_GetMaxDistance();
max_jitter = CNF_GetMaxJitter();
reselect_distance = CNF_GetReselectDistance(); reselect_distance = CNF_GetReselectDistance();
stratum_weight = CNF_GetStratumWeight(); stratum_weight = CNF_GetStratumWeight();
combine_limit = CNF_GetCombineLimit(); combine_limit = CNF_GetCombineLimit();
@@ -308,22 +311,12 @@ SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr)
} }
/* ================================================== */ /* ================================================== */
/* Function to get the range of frequencies, relative to the given
source, that we believe the local clock lies within. The return
values are in terms of the number of seconds fast (+ve) or slow
(-ve) relative to the source that the local clock becomes after a
given amount of local time has elapsed.
Suppose the initial offset relative to the source is U (fast +ve, SST_Stats
slow -ve) and a time interval T elapses measured in terms of the SRC_GetSourcestats(SRC_Instance instance)
local clock. Then the error relative to the source at the end of
the interval should lie in the interval [U+T*lo, U+T*hi]. */
void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
{ {
assert(initialised); assert(initialised);
return instance->stats;
SST_GetFrequencyRange(instance->stats, lo, hi);
} }
/* ================================================== */ /* ================================================== */
@@ -341,7 +334,7 @@ void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
void SRC_AccumulateSample void SRC_AccumulateSample
(SRC_Instance inst, (SRC_Instance inst,
struct timeval *sample_time, struct timespec *sample_time,
double offset, double offset,
double peer_delay, double peer_delay,
double peer_dispersion, double peer_dispersion,
@@ -356,7 +349,8 @@ void SRC_AccumulateSample
inst->leap_status = leap_status; inst->leap_status = leap_status;
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d", DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum); source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
root_delay, root_dispersion, stratum);
if (REF_IsLeapSecondClose()) { if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second"); LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
@@ -403,7 +397,7 @@ special_mode_end(void)
/* Check if the source could still have enough samples to be selectable */ /* Check if the source could still have enough samples to be selectable */
if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size + if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size +
SRC_Samples(sources[i]) >= MIN_SAMPLES_FOR_REGRESS) SST_Samples(sources[i]->stats) >= MIN_SAMPLES_FOR_REGRESS)
return 0; return 0;
} }
@@ -513,10 +507,10 @@ mark_ok_sources(SRC_Status status)
/* ================================================== */ /* ================================================== */
static int static int
combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset, 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 *skew)
{ {
struct timeval src_ref_time; struct timespec src_ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew; double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed; double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd; double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
@@ -563,7 +557,7 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
if (sources[index]->status == SRC_OK) if (sources[index]->status == SRC_OK)
sources[index]->status = SRC_UNSELECTED; sources[index]->status = SRC_UNSELECTED;
UTI_DiffTimevalsToDouble(&elapsed, ref_time, &src_ref_time); elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency; src_offset += elapsed * src_frequency;
offset_weight = 1.0 / sources[index]->sel_info.root_distance; offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / src_skew; frequency_weight = 1.0 / src_skew;
@@ -603,12 +597,12 @@ void
SRC_SelectSource(SRC_Instance updated_inst) SRC_SelectSource(SRC_Instance updated_inst)
{ {
struct SelectInfo *si; struct SelectInfo *si;
struct timeval now, ref_time; struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources; 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 n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
int depth, best_depth, trust_depth, best_trust_depth; int depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index; int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source; 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_skew;
double src_root_delay, src_root_dispersion; double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score; double best_lo, best_hi, distance, sel_src_distance, max_score;
@@ -656,7 +650,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
si = &sources[i]->sel_info; si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
&si->lo_limit, &si->hi_limit, &si->root_distance, &si->lo_limit, &si->hi_limit, &si->root_distance,
&si->variance, &first_sample_ago, &si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok); &si->last_sample_ago, &si->select_ok);
if (!si->select_ok) { if (!si->select_ok) {
@@ -673,6 +667,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue; continue;
} }
/* And the same applies for the estimated standard deviation */
if (si->std_dev > max_jitter) {
sources[i]->status = SRC_JITTERY;
continue;
}
sources[i]->status = SRC_OK; /* For now */ sources[i]->status = SRC_OK; /* For now */
if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago) if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago)
@@ -890,6 +890,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[i]->sel_options & SRC_SELECT_REQUIRE) if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
sel_req_source = 0; sel_req_source = 0;
} else if (sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) {
sources[i]->status = SRC_UNTRUSTED;
} else { } else {
sources[i]->status = SRC_FALSETICKER; sources[i]->status = SRC_FALSETICKER;
} }
@@ -906,18 +909,22 @@ SRC_SelectSource(SRC_Instance updated_inst)
return; return;
} }
/* Accept leap second status if more than half of selectable sources agree */ /* Accept leap second status if more than half of selectable (and trusted
for (i = j1 = j2 = 0; i < n_sel_sources; i++) { if there are any) sources agree */
for (i = leap_ins = leap_del = leap_votes = 0; i < n_sel_sources; i++) {
index = sel_sources[i]; index = sel_sources[i];
if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
continue;
leap_votes++;
if (sources[index]->leap_status == LEAP_InsertSecond) if (sources[index]->leap_status == LEAP_InsertSecond)
j1++; leap_ins++;
else if (sources[index]->leap_status == LEAP_DeleteSecond) else if (sources[index]->leap_status == LEAP_DeleteSecond)
j2++; leap_del++;
} }
if (j1 > n_sel_sources / 2) if (leap_ins > leap_votes / 2)
leap_status = LEAP_InsertSecond; leap_status = LEAP_InsertSecond;
else if (j2 > n_sel_sources / 2) else if (leap_del > leap_votes / 2)
leap_status = LEAP_DeleteSecond; leap_status = LEAP_DeleteSecond;
else else
leap_status = LEAP_Normal; leap_status = LEAP_Normal;
@@ -1094,32 +1101,6 @@ SRC_SetReselectDistance(double distance)
} }
} }
/* ================================================== */
double
SRC_PredictOffset(SRC_Instance inst, struct timeval *when)
{
return SST_PredictOffset(inst->stats, when);
}
/* ================================================== */
double
SRC_MinRoundTripDelay(SRC_Instance inst)
{
return SST_MinRoundTripDelay(inst->stats);
}
/* ================================================== */
int
SRC_IsGoodSample(SRC_Instance inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timeval *when)
{
return SST_IsGoodSample(inst->stats, offset, delay, max_delay_dev_ratio,
clock_error, when);
}
/* ================================================== */ /* ================================================== */
/* This routine is registered as a callback with the local clock /* This routine is registered as a callback with the local clock
module, to be called whenever the local clock changes frequency or module, to be called whenever the local clock changes frequency or
@@ -1128,12 +1109,8 @@ SRC_IsGoodSample(SRC_Instance inst, double offset, double delay,
the new regime. */ the new regime. */
static void static void
slew_sources(struct timeval *raw, slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
struct timeval *cooked, double doffset, LCL_ChangeType change_type, void *anything)
double dfreq,
double doffset,
LCL_ChangeType change_type,
void *anything)
{ {
int i; int i;
@@ -1165,6 +1142,39 @@ add_dispersion(double dispersion, void *anything)
} }
} }
/* ================================================== */
static
FILE *open_dumpfile(SRC_Instance inst, const char *mode)
{
FILE *f;
char filename[1024], *dumpdir;
dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0') {
LOG(LOGS_WARN, LOGF_Sources, "dumpdir not specified");
return NULL;
}
/* Include IP address in the name for NTP sources, or reference ID in hex */
if ((inst->type == SRC_NTP &&
snprintf(filename, sizeof (filename), "%s/%s.dat", dumpdir,
source_to_string(inst)) >= sizeof (filename)) ||
(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");
return NULL;
}
f = fopen(filename, mode);
if (!f && mode[0] != 'r')
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file for %s",
source_to_string(inst));
return f;
}
/* ================================================== */ /* ================================================== */
/* This is called to dump out the source measurement registers */ /* This is called to dump out the source measurement registers */
@@ -1172,34 +1182,15 @@ void
SRC_DumpSources(void) SRC_DumpSources(void)
{ {
FILE *out; FILE *out;
int direc_len, file_len;
char *filename;
unsigned int a, b, c, d;
int i; int i;
char *direc;
direc = CNF_GetDumpDir();
direc_len = strlen(direc);
file_len = direc_len + 24;
filename = MallocArray(char, file_len); /* a bit of slack */
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
a = (sources[i]->ref_id) >> 24; out = open_dumpfile(sources[i], "w");
b = ((sources[i]->ref_id) >> 16) & 0xff; if (!out)
c = ((sources[i]->ref_id) >> 8) & 0xff; continue;
d = ((sources[i]->ref_id)) & 0xff; SST_SaveToFile(sources[i]->stats, out);
fclose(out);
snprintf(filename, file_len - 1, "%s/%d.%d.%d.%d.dat", direc, a, b, c, d);
out = fopen(filename, "w");
if (!out) {
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
} else {
SST_SaveToFile(sources[i]->stats, out);
fclose(out);
}
} }
Free(filename);
} }
/* ================================================== */ /* ================================================== */
@@ -1208,40 +1199,63 @@ void
SRC_ReloadSources(void) SRC_ReloadSources(void)
{ {
FILE *in; FILE *in;
char *filename;
unsigned int a, b, c, d;
int i; int i;
char *dumpdir;
int dumpdirlen, filelen;
for (i=0; i<n_sources; i++) { for (i = 0; i < n_sources; i++) {
a = (sources[i]->ref_id) >> 24; in = open_dumpfile(sources[i], "r");
b = ((sources[i]->ref_id) >> 16) & 0xff; if (!in)
c = ((sources[i]->ref_id) >> 8) & 0xff; continue;
d = ((sources[i]->ref_id)) & 0xff; if (!SST_LoadFromFile(sources[i]->stats, in))
LOG(LOGS_WARN, LOGF_Sources, "Could not load dump file for %s",
dumpdir = CNF_GetDumpDir(); source_to_string(sources[i]));
dumpdirlen = strlen(dumpdir); else
filelen = dumpdirlen + 24; LOG(LOGS_INFO, LOGF_Sources, "Loaded dump file for %s",
filename = MallocArray(char, filelen); source_to_string(sources[i]));
snprintf(filename, filelen-1, "%s/%d.%d.%d.%d.dat", dumpdir, a, b, c, d); fclose(in);
in = fopen(filename, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
} else {
if (SST_LoadFromFile(sources[i]->stats, in)) {
SST_DoNewRegression(sources[i]->stats);
} else {
LOG(LOGS_WARN, LOGF_Sources, "Problem loading from file %s", filename);
}
fclose(in);
}
Free(filename);
} }
} }
/* ================================================== */ /* ================================================== */
void
SRC_RemoveDumpFiles(void)
{
char pattern[1024], name[64], *dumpdir, *s;
IPAddr ip_addr;
glob_t gl;
size_t i;
dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0' ||
snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern))
return;
if (glob(pattern, 0, NULL, &gl))
return;
for (i = 0; i < gl.gl_pathc; i++) {
s = strrchr(gl.gl_pathv[i], '/');
if (!s || snprintf(name, sizeof (name), "%s", s + 1) >= sizeof (name))
continue;
/* Remove .dat extension */
if (strlen(name) < 4)
continue;
name[strlen(name) - 4] = '\0';
/* Check if it looks like name of an actual dump file */
if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
continue;
DEBUG_LOG(LOGF_Sources, "Removing %s", gl.gl_pathv[i]);
unlink(gl.gl_pathv[i]);
}
globfree(&gl);
}
/* ================================================== */
int int
SRC_IsSyncPeer(SRC_Instance inst) SRC_IsSyncPeer(SRC_Instance inst)
{ {
@@ -1286,7 +1300,7 @@ SRC_ActiveSources(void)
/* ================================================== */ /* ================================================== */
int int
SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now) SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
{ {
SRC_Instance src; SRC_Instance src;
if ((index >= n_sources) || (index < 0)) { if ((index >= n_sources) || (index < 0)) {
@@ -1294,7 +1308,6 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
} else { } else {
src = sources[index]; src = sources[index];
memset(&report->ip_addr, 0, sizeof (report->ip_addr));
if (src->ip_addr) if (src->ip_addr)
report->ip_addr = *src->ip_addr; report->ip_addr = *src->ip_addr;
else { else {
@@ -1304,20 +1317,13 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
} }
switch (src->status) { switch (src->status) {
case SRC_UNSELECTABLE:
case SRC_BAD_STATS:
case SRC_BAD_DISTANCE:
case SRC_STALE:
case SRC_ORPHAN:
case SRC_WAITS_STATS:
report->state = RPT_UNREACH;
break;
case SRC_FALSETICKER: case SRC_FALSETICKER:
report->state = RPT_FALSETICKER; report->state = RPT_FALSETICKER;
break; break;
case SRC_JITTERY: case SRC_JITTERY:
report->state = RPT_JITTERY; report->state = RPT_JITTERY;
break; break;
case SRC_UNTRUSTED:
case SRC_WAITS_SOURCES: case SRC_WAITS_SOURCES:
case SRC_NONPREFERRED: case SRC_NONPREFERRED:
case SRC_WAITS_UPDATE: case SRC_WAITS_UPDATE:
@@ -1331,9 +1337,8 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
case SRC_SELECTED: case SRC_SELECTED:
report->state = RPT_SYNC; report->state = RPT_SYNC;
break; break;
case SRC_OK:
default: default:
assert(0); report->state = RPT_UNREACH;
break; break;
} }
@@ -1351,7 +1356,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
/* ================================================== */ /* ================================================== */
int int
SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now) SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now)
{ {
SRC_Instance src; SRC_Instance src;
@@ -1380,11 +1385,3 @@ SRC_GetType(int index)
} }
/* ================================================== */ /* ================================================== */
int
SRC_Samples(SRC_Instance inst)
{
return SST_Samples(inst->stats);
}
/* ================================================== */

View File

@@ -34,6 +34,7 @@
#include "ntp.h" #include "ntp.h"
#include "reports.h" #include "reports.h"
#include "sourcestats.h"
/* Size of the source reachability register */ /* Size of the source reachability register */
#define SOURCE_REACH_BITS 8 #define SOURCE_REACH_BITS 8
@@ -73,18 +74,8 @@ extern void SRC_ResetInstance(SRC_Instance instance);
/* Function to change the sources's reference ID and IP address */ /* Function to change the sources's reference ID and IP address */
extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr); extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
/* Function to get the range of frequencies, relative to the given /* Function to get access to the sourcestats instance */
source, that we believe the local clock lies within. The return extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
values are in terms of the number of seconds fast (+ve) or slow
(-ve) relative to the source that the local clock becomes after a
given amount of local time has elapsed.
Suppose the initial offset relative to the source is U (fast +ve,
slow -ve) and a time interval T elapses measured in terms of the
local clock. Then the error relative to the source at the end of
the interval should lie in the interval [U+T*lo, U+T*hi]. */
extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi);
/* This function is called by one of the source drivers when it has /* This function is called by one of the source drivers when it has
a new sample that is to be accumulated. a new sample that is to be accumulated.
@@ -114,7 +105,7 @@ extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
*/ */
extern void SRC_AccumulateSample(SRC_Instance instance, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status); 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);
/* This routine sets the source as receiving reachability updates */ /* This routine sets the source as receiving reachability updates */
extern void SRC_SetActive(SRC_Instance inst); extern void SRC_SetActive(SRC_Instance inst);
@@ -143,34 +134,18 @@ extern void SRC_ReselectSource(void);
/* Set reselect distance */ /* Set reselect distance */
extern void SRC_SetReselectDistance(double distance); extern void SRC_SetReselectDistance(double distance);
/* Predict the offset of the local clock relative to a given source at
a given local cooked time. Positive indicates local clock is FAST
relative to reference. */
extern double SRC_PredictOffset(SRC_Instance inst, struct timeval *when);
/* Return the minimum peer delay amongst the previous samples
currently held in the register */
extern double SRC_MinRoundTripDelay(SRC_Instance inst);
/* This routine determines if a new sample is good enough that it should be
accumulated */
extern int SRC_IsGoodSample(SRC_Instance inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when);
extern void SRC_DumpSources(void); extern void SRC_DumpSources(void);
extern void SRC_ReloadSources(void); extern void SRC_ReloadSources(void);
extern void SRC_RemoveDumpFiles(void);
extern int SRC_IsSyncPeer(SRC_Instance inst); extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_IsReachable(SRC_Instance inst); extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void); extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void); extern int SRC_ActiveSources(void);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now); extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now); extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
extern SRC_Type SRC_GetType(int index); extern SRC_Type SRC_GetType(int index);
extern int SRC_Samples(SRC_Instance inst);
#endif /* GOT_SOURCES_H */ #endif /* GOT_SOURCES_H */

View File

@@ -47,8 +47,25 @@
2000ppm, which would be pretty bad */ 2000ppm, which would be pretty bad */
#define WORST_CASE_FREQ_BOUND (2000.0/1.0e6) #define WORST_CASE_FREQ_BOUND (2000.0/1.0e6)
/* The minimum allowed skew */ /* The minimum and maximum assumed skew */
#define MIN_SKEW 1.0e-12 #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 asymmetry of network jitter when all jitter is in one direction */
#define MAX_ASYMMETRY 0.5
/* The minimum estimated asymmetry that can activate the offset correction */
#define MIN_ASYMMETRY 0.45
/* The minimum number of consecutive asymmetries with the same sign needed
to activate the offset correction */
#define MIN_ASYMMETRY_RUN 10
/* The maximum value of the counter */
#define MAX_ASYMMETRY_RUN 1000
/* ================================================== */ /* ================================================== */
@@ -72,8 +89,8 @@ struct SST_Stats_Record {
buffer. */ buffer. */
int n_samples; int n_samples;
/* Number of extra samples stored in sample_times and offsets arrays that are /* Number of extra samples stored in sample_times, offsets and peer_delays
used to extend runs test */ arrays that are used to extend the runs test */
int runs_samples; int runs_samples;
/* The index of the newest sample */ /* The index of the newest sample */
@@ -92,11 +109,18 @@ struct SST_Stats_Record {
/* This is the estimated offset (+ve => local fast) at a particular time */ /* This is the estimated offset (+ve => local fast) at a particular time */
double estimated_offset; double estimated_offset;
double estimated_offset_sd; double estimated_offset_sd;
struct timeval offset_time; struct timespec offset_time;
/* Number of runs of the same sign amongst the residuals */ /* Number of runs of the same sign amongst the residuals */
int nruns; int nruns;
/* Number of consecutive estimated asymmetries with the same sign.
The sign of the number encodes the sign of the asymmetry. */
int asymmetry_run;
/* This is the latest estimated asymmetry of network jitter */
double asymmetry;
/* This value contains the estimated frequency. This is the number /* This value contains the estimated frequency. This is the number
of seconds that the local clock gains relative to the reference of seconds that the local clock gains relative to the reference
source per unit local time. (Positive => local clock fast, source per unit local time. (Positive => local clock fast,
@@ -108,12 +132,12 @@ struct SST_Stats_Record {
about estimated_frequency */ about estimated_frequency */
double skew; double skew;
/* This is the estimated residual variance of the data points */ /* This is the estimated standard deviation of the data points */
double variance; double std_dev;
/* This array contains the sample epochs, in terms of the local /* This array contains the sample epochs, in terms of the local
clock. */ clock. */
struct timeval sample_times[MAX_SAMPLES * REGRESS_RUNS_RATIO]; struct timespec sample_times[MAX_SAMPLES * REGRESS_RUNS_RATIO];
/* This is an array of offsets, in seconds, corresponding to the /* This is an array of offsets, in seconds, corresponding to the
sample times. In this module, we use the convention that sample times. In this module, we use the convention that
@@ -131,7 +155,7 @@ struct SST_Stats_Record {
/* This is an array of peer delays, in seconds, being the roundtrip /* This is an array of peer delays, in seconds, being the roundtrip
measurement delay to the peer */ measurement delay to the peer */
double peer_delays[MAX_SAMPLES]; double peer_delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
/* This is an array of peer dispersions, being the skew and local /* This is an array of peer dispersions, being the skew and local
precision dispersion terms from sampling the peer */ precision dispersion terms from sampling the peer */
@@ -161,7 +185,7 @@ void
SST_Initialise(void) SST_Initialise(void)
{ {
logfileid = CNF_GetLogStatistics() ? LOG_FileOpen("statistics", logfileid = CNF_GetLogStatistics() ? LOG_FileOpen("statistics",
" Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr") " Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr Asym")
: -1; : -1;
} }
@@ -214,10 +238,11 @@ SST_ResetInstance(SST_Stats inst)
inst->skew = 2000.0e-6; inst->skew = 2000.0e-6;
inst->estimated_offset = 0.0; inst->estimated_offset = 0.0;
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */ inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
inst->offset_time.tv_sec = 0; UTI_ZeroTimespec(&inst->offset_time);
inst->offset_time.tv_usec = 0; inst->std_dev = 4.0;
inst->variance = 16.0;
inst->nruns = 0; inst->nruns = 0;
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
} }
/* ================================================== */ /* ================================================== */
@@ -253,7 +278,7 @@ prune_register(SST_Stats inst, int new_oldest)
/* ================================================== */ /* ================================================== */
void void
SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
double offset, double offset,
double peer_delay, double peer_dispersion, double peer_delay, double peer_dispersion,
double root_delay, double root_dispersion, double root_delay, double root_dispersion,
@@ -269,7 +294,7 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
/* Make sure it's newer than the last sample */ /* Make sure it's newer than the last sample */
if (inst->n_samples && if (inst->n_samples &&
UTI_CompareTimevals(&inst->sample_times[inst->last_sample], sample_time) >= 0) { 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", LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid)); inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
SST_ResetInstance(inst); SST_ResetInstance(inst);
@@ -282,14 +307,14 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
inst->sample_times[n] = *sample_time; inst->sample_times[n] = *sample_time;
inst->offsets[n] = offset; inst->offsets[n] = offset;
inst->orig_offsets[m] = offset; inst->orig_offsets[m] = offset;
inst->peer_delays[m] = peer_delay; inst->peer_delays[n] = peer_delay;
inst->peer_dispersions[m] = peer_dispersion; inst->peer_dispersions[m] = peer_dispersion;
inst->root_delays[m] = root_delay; inst->root_delays[m] = root_delay;
inst->root_dispersions[m] = root_dispersion; inst->root_dispersions[m] = root_dispersion;
inst->strata[m] = stratum; inst->strata[m] = stratum;
if (!inst->n_samples || inst->peer_delays[m] < inst->peer_delays[inst->min_delay_sample]) if (!inst->n_samples || inst->peer_delays[n] < inst->peer_delays[inst->min_delay_sample])
inst->min_delay_sample = m; inst->min_delay_sample = n;
++inst->n_samples; ++inst->n_samples;
} }
@@ -323,14 +348,13 @@ get_buf_index(SST_Stats inst, int i)
static void static void
convert_to_intervals(SST_Stats inst, double *times_back) convert_to_intervals(SST_Stats inst, double *times_back)
{ {
struct timeval *newest_tv; struct timespec *ts;
int i; int i;
newest_tv = &(inst->sample_times[inst->last_sample]); ts = &inst->sample_times[inst->last_sample];
for (i = -inst->runs_samples; i < inst->n_samples; i++) { for (i = -inst->runs_samples; i < inst->n_samples; i++) {
/* The entries in times_back[] should end up negative */ /* The entries in times_back[] should end up negative */
UTI_DiffTimevalsToDouble(&times_back[i], times_back[i] = UTI_DiffTimespecsToDouble(&inst->sample_times[get_runsbuf_index(inst, i)], ts);
&inst->sample_times[get_runsbuf_index(inst, i)], newest_tv);
} }
} }
@@ -376,15 +400,65 @@ find_min_delay_sample(SST_Stats inst)
{ {
int i, index; int i, index;
inst->min_delay_sample = get_buf_index(inst, 0); inst->min_delay_sample = get_runsbuf_index(inst, -inst->runs_samples);
for (i = 1; i < inst->n_samples; i++) { for (i = -inst->runs_samples + 1; i < inst->n_samples; i++) {
index = get_buf_index(inst, i); index = get_runsbuf_index(inst, i);
if (inst->peer_delays[index] < inst->peer_delays[inst->min_delay_sample]) if (inst->peer_delays[index] < inst->peer_delays[inst->min_delay_sample])
inst->min_delay_sample = index; inst->min_delay_sample = index;
} }
} }
/* ================================================== */
/* This function estimates asymmetry of network jitter on the path to the
source as a slope of offset against network delay in multiple linear
regression. If the asymmetry is significant and its sign doesn't change
frequently, the measured offsets (which are used later to estimate the
offset and frequency of the clock) are corrected to correspond to the
minimum network delay. This can significantly improve the accuracy and
stability of the estimated offset and frequency. */
static void
correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
{
double asymmetry, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
int i, n;
/* Don't try to estimate the asymmetry with reference clocks */
if (!inst->ip_addr)
return;
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];
/* 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;
}
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;
}
/* ================================================== */ /* ================================================== */
/* This defines the assumed ratio between the standard deviation of /* This defines the assumed ratio between the standard deviation of
@@ -425,7 +499,8 @@ SST_DoNewRegression(SST_Stats inst)
for (i = 0, mean_distance = 0.0, min_distance = DBL_MAX; i < inst->n_samples; i++) { for (i = 0, mean_distance = 0.0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
j = get_buf_index(inst, i); j = get_buf_index(inst, i);
peer_distances[i] = 0.5 * inst->peer_delays[j] + inst->peer_dispersions[j]; peer_distances[i] = 0.5 * inst->peer_delays[get_runsbuf_index(inst, i)] +
inst->peer_dispersions[j];
mean_distance += peer_distances[i]; mean_distance += peer_distances[i];
if (peer_distances[i] < min_distance) { if (peer_distances[i] < min_distance) {
min_distance = peer_distances[i]; min_distance = peer_distances[i];
@@ -436,8 +511,7 @@ SST_DoNewRegression(SST_Stats inst)
/* And now, work out the weight vector */ /* And now, work out the weight vector */
sd = mean_distance - min_distance; sd = mean_distance - min_distance;
if (sd > min_distance || sd <= 0.0) sd = CLAMP(MIN_WEIGHT_SD, sd, min_distance);
sd = min_distance;
for (i=0; i<inst->n_samples; i++) { for (i=0; i<inst->n_samples; i++) {
sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / sd; sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / sd;
@@ -445,6 +519,8 @@ SST_DoNewRegression(SST_Stats inst)
} }
} }
correct_asymmetry(inst, times_back, offsets);
inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples, inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples,
offsets + inst->runs_samples, weights, offsets + inst->runs_samples, weights,
inst->n_samples, inst->runs_samples, inst->n_samples, inst->runs_samples,
@@ -463,26 +539,26 @@ SST_DoNewRegression(SST_Stats inst)
inst->estimated_offset = est_intercept; inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample]; inst->offset_time = inst->sample_times[inst->last_sample];
inst->estimated_offset_sd = est_intercept_sd; inst->estimated_offset_sd = est_intercept_sd;
inst->variance = est_var; inst->std_dev = sqrt(est_var);
inst->nruns = nruns; inst->nruns = nruns;
if (inst->skew < MIN_SKEW) inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
inst->skew = MIN_SKEW;
stress = fabs(old_freq - inst->estimated_frequency) / old_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",
inst->estimated_offset, inst->estimated_frequency, inst->skew,
inst->n_samples, best_start, inst->nruns,
inst->asymmetry, inst->asymmetry_run);
if (logfileid != -1) { if (logfileid != -1) {
LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d", LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d %5.2f",
UTI_TimeToLogForm(inst->offset_time.tv_sec), UTI_TimeToLogForm(inst->offset_time.tv_sec),
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid), inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid),
sqrt(inst->variance), inst->std_dev,
inst->estimated_offset, inst->estimated_offset, inst->estimated_offset_sd,
inst->estimated_offset_sd, inst->estimated_frequency, inst->skew, stress,
inst->estimated_frequency, inst->n_samples, best_start, inst->nruns,
inst->skew, inst->asymmetry);
stress,
inst->n_samples,
best_start, nruns);
} }
times_back_start = inst->runs_samples + best_start; times_back_start = inst->runs_samples + best_start;
@@ -524,12 +600,12 @@ SST_GetFrequencyRange(SST_Stats inst,
/* ================================================== */ /* ================================================== */
void void
SST_GetSelectionData(SST_Stats inst, struct timeval *now, SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum, int *stratum,
double *offset_lo_limit, double *offset_lo_limit,
double *offset_hi_limit, double *offset_hi_limit,
double *root_distance, double *root_distance,
double *variance, double *std_dev,
double *first_sample_ago, double *first_sample_ago,
double *last_sample_ago, double *last_sample_ago,
int *select_ok) int *select_ok)
@@ -546,9 +622,9 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
j = get_buf_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->strata[get_buf_index(inst, inst->n_samples - 1)];
*variance = inst->variance; *std_dev = inst->std_dev;
UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[i]); sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency; offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency;
*root_distance = 0.5 * inst->root_delays[j] + *root_distance = 0.5 * inst->root_delays[j] +
inst->root_dispersions[j] + sample_elapsed * inst->skew; inst->root_dispersions[j] + sample_elapsed * inst->skew;
@@ -560,10 +636,10 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
double average_offset, elapsed; double average_offset, elapsed;
int average_ok; int average_ok;
/* average_ok ignored for now */ /* average_ok ignored for now */
UTI_DiffTimevalsToDouble(&elapsed, now, &(inst->offset_time)); elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed; average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed;
if (fabs(average_offset - offset) <= if (fabs(average_offset - offset) <=
inst->peer_dispersions[j] + 0.5 * inst->peer_delays[j]) { inst->peer_dispersions[j] + 0.5 * inst->peer_delays[i]) {
average_ok = 1; average_ok = 1;
} else { } else {
average_ok = 0; average_ok = 0;
@@ -571,21 +647,21 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
#endif #endif
i = get_runsbuf_index(inst, 0); i = get_runsbuf_index(inst, 0);
UTI_DiffTimevalsToDouble(first_sample_ago, now, &inst->sample_times[i]); *first_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
i = get_runsbuf_index(inst, inst->n_samples - 1); i = get_runsbuf_index(inst, inst->n_samples - 1);
UTI_DiffTimevalsToDouble(last_sample_ago, now, &inst->sample_times[i]); *last_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
*select_ok = inst->regression_ok; *select_ok = inst->regression_ok;
DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f var=%f first_ago=%f last_ago=%f selok=%d", DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
inst->n_samples, offset, *root_distance, *variance, inst->n_samples, offset, *root_distance, *std_dev,
*first_sample_ago, *last_sample_ago, *select_ok); *first_sample_ago, *last_sample_ago, *select_ok);
} }
/* ================================================== */ /* ================================================== */
void void
SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time, SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd, double *average_offset, double *offset_sd,
double *frequency, double *skew, double *frequency, double *skew,
double *root_delay, double *root_dispersion) double *root_delay, double *root_dispersion)
@@ -605,7 +681,7 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
*skew = inst->skew; *skew = inst->skew;
*root_delay = inst->root_delays[j]; *root_delay = inst->root_delays[j];
UTI_DiffTimevalsToDouble(&elapsed_sample, &inst->offset_time, &inst->sample_times[i]); elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample; *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", DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
@@ -616,11 +692,11 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
/* ================================================== */ /* ================================================== */
void void
SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset) SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset)
{ {
int m, i; int m, i;
double delta_time; double delta_time;
struct timeval *sample, prev; struct timespec *sample, prev;
double prev_offset, prev_freq; double prev_offset, prev_freq;
if (!inst->n_samples) if (!inst->n_samples)
@@ -628,9 +704,9 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
for (m = -inst->runs_samples; m < inst->n_samples; m++) { for (m = -inst->runs_samples; m < inst->n_samples; m++) {
i = get_runsbuf_index(inst, m); i = get_runsbuf_index(inst, m);
sample = &(inst->sample_times[i]); sample = &inst->sample_times[i];
prev = *sample; prev = *sample;
UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset); UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
inst->offsets[i] += delta_time; inst->offsets[i] += delta_time;
} }
@@ -638,14 +714,14 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
prev = inst->offset_time; prev = inst->offset_time;
prev_offset = inst->estimated_offset; prev_offset = inst->estimated_offset;
prev_freq = inst->estimated_frequency; prev_freq = inst->estimated_frequency;
UTI_AdjustTimeval(&(inst->offset_time), when, &(inst->offset_time), UTI_AdjustTimespec(&inst->offset_time, when, &inst->offset_time,
&delta_time, dfreq, doffset); &delta_time, dfreq, doffset);
inst->estimated_offset += delta_time; inst->estimated_offset += delta_time;
inst->estimated_frequency = (inst->estimated_frequency - dfreq) / (1.0 - dfreq); 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(LOGF_SourceStats, "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, inst->n_samples, inst->runs_samples,
UTI_TimevalToString(&prev), UTI_TimevalToString(&(inst->offset_time)), UTI_TimespecToString(&prev), UTI_TimespecToString(&inst->offset_time),
prev_offset, inst->estimated_offset, prev_offset, inst->estimated_offset,
1.0e6 * prev_freq, 1.0e6 * inst->estimated_frequency); 1.0e6 * prev_freq, 1.0e6 * inst->estimated_frequency);
} }
@@ -667,7 +743,7 @@ SST_AddDispersion(SST_Stats inst, double dispersion)
/* ================================================== */ /* ================================================== */
double double
SST_PredictOffset(SST_Stats inst, struct timeval *when) SST_PredictOffset(SST_Stats inst, struct timespec *when)
{ {
double elapsed; double elapsed;
@@ -681,7 +757,7 @@ SST_PredictOffset(SST_Stats inst, struct timeval *when)
return 0.0; return 0.0;
} }
} else { } else {
UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time); elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
return inst->estimated_offset + elapsed * inst->estimated_frequency; return inst->estimated_offset + elapsed * inst->estimated_frequency;
} }
@@ -701,20 +777,20 @@ SST_MinRoundTripDelay(SST_Stats inst)
int int
SST_IsGoodSample(SST_Stats inst, double offset, double delay, SST_IsGoodSample(SST_Stats inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timeval *when) double max_delay_dev_ratio, double clock_error, struct timespec *when)
{ {
double elapsed, allowed_increase, delay_increase; double elapsed, allowed_increase, delay_increase;
if (inst->n_samples < 3) if (inst->n_samples < 3)
return 1; return 1;
UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time); elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
/* Require that the ratio of the increase in delay from the minimum to the /* 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 standard deviation is less than max_delay_dev_ratio. In the allowed
increase in delay include also skew and clock_error. */ increase in delay include also skew and clock_error. */
allowed_increase = sqrt(inst->variance) * max_delay_dev_ratio + allowed_increase = inst->std_dev * max_delay_dev_ratio +
elapsed * (inst->skew + clock_error); elapsed * (inst->skew + clock_error);
delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0; delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0;
@@ -750,12 +826,18 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
i = get_runsbuf_index(inst, m); i = get_runsbuf_index(inst, m);
j = get_buf_index(inst, m); j = get_buf_index(inst, m);
fprintf(out, "%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n", fprintf(out,
(unsigned long) inst->sample_times[i].tv_sec, #ifdef HAVE_LONG_TIME_T
(unsigned long) inst->sample_times[i].tv_usec, "%08"PRIx64" %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
(uint64_t)inst->sample_times[i].tv_sec,
#else
"%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
(unsigned long)inst->sample_times[i].tv_sec,
#endif
(unsigned long)inst->sample_times[i].tv_nsec / 1000,
inst->offsets[i], inst->offsets[i],
inst->orig_offsets[j], inst->orig_offsets[j],
inst->peer_delays[j], inst->peer_delays[i],
inst->peer_dispersions[j], inst->peer_dispersions[j],
inst->root_delays[j], inst->root_delays[j],
inst->root_dispersions[j], inst->root_dispersions[j],
@@ -763,6 +845,8 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
inst->strata[j]); inst->strata[j]);
} }
fprintf(out, "%d\n", inst->asymmetry_run);
} }
/* ================================================== */ /* ================================================== */
@@ -771,22 +855,30 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
int int
SST_LoadFromFile(SST_Stats inst, FILE *in) SST_LoadFromFile(SST_Stats inst, FILE *in)
{ {
int i, line_number; #ifdef HAVE_LONG_TIME_T
uint64_t sec;
#else
unsigned long sec;
#endif
unsigned long usec;
int i;
char line[1024]; char line[1024];
unsigned long sec, usec;
double weight; double weight;
assert(!inst->n_samples); assert(!inst->n_samples);
if (fgets(line, sizeof(line), in) && if (fgets(line, sizeof(line), in) &&
sscanf(line, "%d", &inst->n_samples) == 1 && sscanf(line, "%d", &inst->n_samples) == 1 &&
inst->n_samples > 0 && inst->n_samples <= MAX_SAMPLES) { inst->n_samples >= 0 && inst->n_samples <= MAX_SAMPLES) {
line_number = 2;
for (i=0; i<inst->n_samples; i++) { for (i=0; i<inst->n_samples; i++) {
if (!fgets(line, sizeof(line), in) || if (!fgets(line, sizeof(line), in) ||
(sscanf(line, "%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n", (sscanf(line,
#ifdef HAVE_LONG_TIME_T
"%"SCNx64"%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
#else
"%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
#endif
&(sec), &(usec), &(sec), &(usec),
&(inst->offsets[i]), &(inst->offsets[i]),
&(inst->orig_offsets[i]), &(inst->orig_offsets[i]),
@@ -799,40 +891,44 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
/* This is the branch taken if the read FAILED */ /* This is the branch taken if the read FAILED */
LOG(LOGS_WARN, LOGF_SourceStats, "Failed to read data from line %d of dump file", line_number);
inst->n_samples = 0; /* Load abandoned if any sign of corruption */ inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0; return 0;
} else { } else {
/* This is the branch taken if the read is SUCCESSFUL */ /* This is the branch taken if the read is SUCCESSFUL */
inst->sample_times[i].tv_sec = sec; inst->sample_times[i].tv_sec = sec;
inst->sample_times[i].tv_usec = usec; inst->sample_times[i].tv_nsec = 1000 * usec;
UTI_NormaliseTimespec(&inst->sample_times[i]);
line_number++;
} }
} }
/* This field was not saved in older versions */
if (!fgets(line, sizeof(line), in) || sscanf(line, "%d\n", &inst->asymmetry_run) != 1)
inst->asymmetry_run = 0;
} else { } else {
LOG(LOGS_WARN, LOGF_SourceStats, "Could not read number of samples from dump file");
inst->n_samples = 0; /* Load abandoned if any sign of corruption */ inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0; return 0;
} }
if (!inst->n_samples)
return 1;
inst->last_sample = inst->n_samples - 1; inst->last_sample = inst->n_samples - 1;
inst->runs_samples = 0; inst->runs_samples = 0;
find_min_delay_sample(inst); find_min_delay_sample(inst);
SST_DoNewRegression(inst);
return 1; return 1;
} }
/* ================================================== */ /* ================================================== */
void void
SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now) SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now)
{ {
int i, j; int i, j;
struct timeval ago; struct timespec ago;
if (inst->n_samples > 0) { if (inst->n_samples > 0) {
i = get_runsbuf_index(inst, inst->n_samples - 1); i = get_runsbuf_index(inst, inst->n_samples - 1);
@@ -842,7 +938,7 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j]; report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j]; report->stratum = inst->strata[j];
UTI_DiffTimevals(&ago, now, &inst->sample_times[i]); UTI_DiffTimespecs(&ago, now, &inst->sample_times[i]);
report->latest_meas_ago = ago.tv_sec; report->latest_meas_ago = ago.tv_sec;
} else { } else {
report->latest_meas_ago = (uint32_t)-1; report->latest_meas_ago = (uint32_t)-1;
@@ -864,7 +960,7 @@ SST_Samples(SST_Stats inst)
/* ================================================== */ /* ================================================== */
void void
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timeval *now) SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
{ {
double dspan; double dspan;
double elapsed, sample_elapsed; double elapsed, sample_elapsed;
@@ -876,15 +972,15 @@ SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct ti
if (inst->n_samples > 1) { if (inst->n_samples > 1) {
li = get_runsbuf_index(inst, inst->n_samples - 1); li = get_runsbuf_index(inst, inst->n_samples - 1);
lj = get_buf_index(inst, inst->n_samples - 1); lj = get_buf_index(inst, inst->n_samples - 1);
UTI_DiffTimevalsToDouble(&dspan, &inst->sample_times[li], dspan = UTI_DiffTimespecsToDouble(&inst->sample_times[li],
&inst->sample_times[get_runsbuf_index(inst, 0)]); &inst->sample_times[get_runsbuf_index(inst, 0)]);
report->span_seconds = (unsigned long) (dspan + 0.5); report->span_seconds = (unsigned long) (dspan + 0.5);
if (inst->n_samples > 3) { if (inst->n_samples > 3) {
UTI_DiffTimevalsToDouble(&elapsed, now, &inst->offset_time); elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
bi = get_runsbuf_index(inst, inst->best_single_sample); bi = get_runsbuf_index(inst, inst->best_single_sample);
bj = get_buf_index(inst, inst->best_single_sample); bj = get_buf_index(inst, inst->best_single_sample);
UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[bi]); sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[bi]);
report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency; report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency;
report->est_offset_err = (inst->estimated_offset_sd + report->est_offset_err = (inst->estimated_offset_sd +
sample_elapsed * inst->skew + sample_elapsed * inst->skew +
@@ -901,7 +997,15 @@ SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct ti
report->resid_freq_ppm = 1.0e6 * inst->estimated_frequency; report->resid_freq_ppm = 1.0e6 * inst->estimated_frequency;
report->skew_ppm = 1.0e6 * inst->skew; report->skew_ppm = 1.0e6 * inst->skew;
report->sd = sqrt(inst->variance); report->sd = inst->std_dev;
}
/* ================================================== */
double
SST_GetJitterAsymmetry(SST_Stats inst)
{
return inst->asymmetry;
} }
/* ================================================== */ /* ================================================== */

View File

@@ -61,7 +61,7 @@ extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
stratum is the stratum of the source from which the sample came. stratum is the stratum of the source from which the sample came.
*/ */
extern void SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum); 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 runs the linear regression operation on the data. It /* This function runs the linear regression operation on the data. It
finds the set of most recent samples that give the tightest finds the set of most recent samples that give the tightest
@@ -77,7 +77,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */ /* Get data needed for selection */
extern void extern void
SST_GetSelectionData(SST_Stats inst, struct timeval *now, SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum, int *stratum,
double *offset_lo_limit, double *offset_lo_limit,
double *offset_hi_limit, double *offset_hi_limit,
@@ -89,7 +89,7 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
/* Get data needed when setting up tracking on this source */ /* Get data needed when setting up tracking on this source */
extern void extern void
SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time, SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd, double *average_offset, double *offset_sd,
double *frequency, double *skew, double *frequency, double *skew,
double *root_delay, double *root_dispersion); double *root_delay, double *root_dispersion);
@@ -110,7 +110,7 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
*/ */
extern void SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset); extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset);
/* This routine is called when an indeterminate offset is introduced /* This routine is called when an indeterminate offset is introduced
into the local time. */ into the local time. */
@@ -119,7 +119,7 @@ extern void SST_AddDispersion(SST_Stats inst, double dispersion);
/* Predict the offset of the local clock relative to a given source at /* Predict the offset of the local clock relative to a given source at
a given local cooked time. Positive indicates local clock is FAST a given local cooked time. Positive indicates local clock is FAST
relative to reference. */ relative to reference. */
extern double SST_PredictOffset(SST_Stats inst, struct timeval *when); extern double SST_PredictOffset(SST_Stats inst, struct timespec *when);
/* Find the minimum round trip delay in the register */ /* Find the minimum round trip delay in the register */
extern double SST_MinRoundTripDelay(SST_Stats inst); extern double SST_MinRoundTripDelay(SST_Stats inst);
@@ -127,17 +127,19 @@ extern double SST_MinRoundTripDelay(SST_Stats inst);
/* This routine determines if a new sample is good enough that it should be /* This routine determines if a new sample is good enough that it should be
accumulated */ accumulated */
extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay, extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timeval *when); double max_delay_dev_ratio, double clock_error, struct timespec *when);
extern void SST_SaveToFile(SST_Stats inst, FILE *out); extern void SST_SaveToFile(SST_Stats inst, FILE *out);
extern int SST_LoadFromFile(SST_Stats inst, FILE *in); extern int SST_LoadFromFile(SST_Stats inst, FILE *in);
extern void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now); extern void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now);
extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timeval *now); extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now);
extern int SST_Samples(SST_Stats inst); extern int SST_Samples(SST_Stats inst);
extern double SST_GetJitterAsymmetry(SST_Stats inst);
#endif /* GOT_SOURCESTATS_H */ #endif /* GOT_SOURCESTATS_H */

View File

@@ -42,22 +42,24 @@ typedef struct {
int max_sources; int max_sources;
int min_samples; int min_samples;
int max_samples; int max_samples;
int interleaved;
int sel_options; int sel_options;
uint32_t authkey; uint32_t authkey;
double max_delay; double max_delay;
double max_delay_ratio; double max_delay_ratio;
double max_delay_dev_ratio; double max_delay_dev_ratio;
double offset;
} SourceParameters; } SourceParameters;
#define SRC_DEFAULT_PORT 123 #define SRC_DEFAULT_PORT 123
#define SRC_DEFAULT_MINPOLL 6 #define SRC_DEFAULT_MINPOLL 6
#define SRC_DEFAULT_MAXPOLL 10 #define SRC_DEFAULT_MAXPOLL 10
#define SRC_DEFAULT_PRESEND_MINPOLL 0 #define SRC_DEFAULT_PRESEND_MINPOLL 100
#define SRC_DEFAULT_MAXDELAY 3.0 #define SRC_DEFAULT_MAXDELAY 3.0
#define SRC_DEFAULT_MAXDELAYRATIO 0.0 #define SRC_DEFAULT_MAXDELAYRATIO 0.0
#define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0 #define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0
#define SRC_DEFAULT_MINSTRATUM 0 #define SRC_DEFAULT_MINSTRATUM 0
#define SRC_DEFAULT_POLLTARGET 6 #define SRC_DEFAULT_POLLTARGET 8
#define SRC_DEFAULT_MAXSOURCES 4 #define SRC_DEFAULT_MAXSOURCES 4
#define SRC_DEFAULT_MINSAMPLES (-1) #define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1) #define SRC_DEFAULT_MAXSAMPLES (-1)

57
stubs.c
View File

@@ -38,9 +38,11 @@
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_signd.h"
#include "privops.h" #include "privops.h"
#include "refclock.h" #include "refclock.h"
#include "sched.h" #include "sched.h"
#include "util.h"
#ifndef FEAT_ASYNCDNS #ifndef FEAT_ASYNCDNS
@@ -50,10 +52,11 @@ struct DNS_Async_Instance {
const char *name; const char *name;
DNS_NameResolveHandler handler; DNS_NameResolveHandler handler;
void *arg; void *arg;
int pipe[2];
}; };
static void static void
resolve_name(void *anything) resolve_name(int fd, int event, void *anything)
{ {
struct DNS_Async_Instance *inst; struct DNS_Async_Instance *inst;
IPAddr addrs[DNS_MAX_ADDRESSES]; IPAddr addrs[DNS_MAX_ADDRESSES];
@@ -61,6 +64,11 @@ resolve_name(void *anything)
int i; int i;
inst = (struct DNS_Async_Instance *)anything; inst = (struct DNS_Async_Instance *)anything;
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES); status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES);
for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES && for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES &&
@@ -82,7 +90,16 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->handler = handler; inst->handler = handler;
inst->arg = anything; inst->arg = anything;
SCH_AddTimeoutByDelay(0.0, resolve_name, inst); if (pipe(inst->pipe))
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, resolve_name, inst);
if (write(inst->pipe[1], "", 1) < 0)
;
} }
#endif /* !FEAT_ASYNCDNS */ #endif /* !FEAT_ASYNCDNS */
@@ -291,11 +308,17 @@ NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
} }
void void
NSR_ReportSource(RPT_SourceReport *report, struct timeval *now) NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{ {
memset(report, 0, sizeof (*report)); memset(report, 0, sizeof (*report));
} }
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
return 0;
}
void void
NSR_GetActivityReport(RPT_ActivityReport *report) NSR_GetActivityReport(RPT_ActivityReport *report)
{ {
@@ -361,9 +384,35 @@ RCL_StartRefclocks(void)
} }
void void
RCL_ReportSource(RPT_SourceReport *report, struct timeval *now) RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
{ {
memset(report, 0, sizeof (*report)); memset(report, 0, sizeof (*report));
} }
#endif /* !FEAT_REFCLOCK */ #endif /* !FEAT_REFCLOCK */
#ifndef FEAT_SIGND
void
NSD_Initialise(void)
{
}
void
NSD_Finalise(void)
{
}
int
NSD_GetAuthDelay(uint32_t key_id)
{
return 0;
}
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
return 0;
}
#endif /* !FEAT_SIGND */

View File

@@ -71,7 +71,7 @@ static double offset_register;
static double slew_freq; static double slew_freq;
/* Time (raw) of last update of slewing frequency and offset */ /* Time (raw) of last update of slewing frequency and offset */
static struct timeval slew_start; static struct timespec slew_start;
/* Limits for the slew timeout */ /* Limits for the slew timeout */
#define MIN_SLEW_TIMEOUT 1.0 #define MIN_SLEW_TIMEOUT 1.0
@@ -106,7 +106,7 @@ static void update_slew(void);
/* Adjust slew_start on clock step */ /* Adjust slew_start on clock step */
static void static void
handle_step(struct timeval *raw, struct timeval *cooked, double dfreq, handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything) double doffset, LCL_ChangeType change_type, void *anything)
{ {
if (change_type == LCL_ChangeUnknownStep) { if (change_type == LCL_ChangeUnknownStep) {
@@ -115,7 +115,7 @@ handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
offset_register = 0.0; offset_register = 0.0;
update_slew(); update_slew();
} else if (change_type == LCL_ChangeStep) { } else if (change_type == LCL_ChangeStep) {
UTI_AddDoubleToTimeval(&slew_start, -doffset, &slew_start); UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
} }
} }
@@ -138,7 +138,7 @@ start_fastslew(void)
/* ================================================== */ /* ================================================== */
static void static void
stop_fastslew(struct timeval *now) stop_fastslew(struct timespec *now)
{ {
double corr; double corr;
@@ -169,7 +169,7 @@ clamp_freq(double freq)
static void static void
update_slew(void) update_slew(void)
{ {
struct timeval now, end_of_slew; struct timespec now, end_of_slew;
double old_slew_freq, total_freq, corr_freq, duration; double old_slew_freq, total_freq, corr_freq, duration;
/* Remove currently running timeout */ /* Remove currently running timeout */
@@ -178,7 +178,7 @@ update_slew(void)
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
/* Adjust the offset register by achieved slew */ /* Adjust the offset register by achieved slew */
UTI_DiffTimevalsToDouble(&duration, &now, &slew_start); duration = UTI_DiffTimespecsToDouble(&now, &slew_start);
offset_register -= slew_freq * duration; offset_register -= slew_freq * duration;
stop_fastslew(&now); stop_fastslew(&now);
@@ -242,7 +242,7 @@ update_slew(void)
} }
/* Restart timer for the next update */ /* Restart timer for the next update */
UTI_AddDoubleToTimeval(&now, duration, &end_of_slew); UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL); slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
slew_start = now; slew_start = now;
@@ -294,12 +294,12 @@ accrue_offset(double offset, double corr_rate)
/* Determine the correction to generate the cooked time for given raw time */ /* Determine the correction to generate the cooked time for given raw time */
static void static void
offset_convert(struct timeval *raw, offset_convert(struct timespec *raw,
double *corr, double *err) double *corr, double *err)
{ {
double duration, fastslew_corr, fastslew_err; double duration, fastslew_corr, fastslew_err;
UTI_DiffTimevalsToDouble(&duration, raw, &slew_start); duration = UTI_DiffTimespecsToDouble(raw, &slew_start);
if (drv_get_offset_correction && fastslew_active) { if (drv_get_offset_correction && fastslew_active) {
drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err); drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
@@ -324,19 +324,21 @@ offset_convert(struct timeval *raw,
static int static int
apply_step_offset(double offset) apply_step_offset(double offset)
{ {
struct timeval old_time, new_time; struct timespec old_time, new_time;
struct timeval new_time_tv;
double err; double err;
LCL_ReadRawTime(&old_time); LCL_ReadRawTime(&old_time);
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time, NULL) < 0) { if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed"); DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed");
return 0; return 0;
} }
LCL_ReadRawTime(&old_time); LCL_ReadRawTime(&old_time);
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time); err = UTI_DiffTimespecsToDouble(&old_time, &new_time);
lcl_InvokeDispersionNotifyHandlers(fabs(err)); lcl_InvokeDispersionNotifyHandlers(fabs(err));
@@ -403,7 +405,7 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
void void
SYS_Generic_Finalise(void) SYS_Generic_Finalise(void)
{ {
struct timeval now; struct timespec now;
/* Must *NOT* leave a slew running - clock could drift way off /* Must *NOT* leave a slew running - clock could drift way off
if the daemon is not restarted */ if the daemon is not restarted */

View File

@@ -51,7 +51,7 @@
#include <sys/prctl.h> #include <sys/prctl.h>
#include <seccomp.h> #include <seccomp.h>
#include <termios.h> #include <termios.h>
#ifdef FEAT_PHC #if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h> #include <linux/ptp_clock.h>
#endif #endif
#ifdef FEAT_PPS #ifdef FEAT_PPS
@@ -60,6 +60,9 @@
#ifdef FEAT_RTC #ifdef FEAT_RTC
#include <linux/rtc.h> #include <linux/rtc.h>
#endif #endif
#ifdef HAVE_LINUX_TIMESTAMPING
#include <linux/sockios.h>
#endif
#endif #endif
#include "sys_linux.h" #include "sys_linux.h"
@@ -271,6 +274,22 @@ kernelvercmp(int major1, int minor1, int patch1,
} }
/* ================================================== */ /* ================================================== */
static void
get_kernel_version(int *major, int *minor, int *patch)
{
struct utsname uts;
if (uname(&uts) < 0)
LOG_FATAL(LOGF_SysLinux, "uname() failed");
*patch = 0;
if (sscanf(uts.release, "%d.%d.%d", major, minor, patch) < 2)
LOG_FATAL(LOGF_SysLinux, "Could not parse kernel version");
}
/* ================================================== */
/* Compute the scaling to use on any frequency we set, according to /* Compute the scaling to use on any frequency we set, according to
the vintage of the Linux kernel being used. */ the vintage of the Linux kernel being used. */
@@ -278,7 +297,6 @@ static void
get_version_specific_details(void) get_version_specific_details(void)
{ {
int major, minor, patch; int major, minor, patch;
struct utsname uts;
hz = get_hz(); hz = get_hz();
@@ -293,15 +311,7 @@ get_version_specific_details(void)
(CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */ (CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
tick_update_hz = 100; tick_update_hz = 100;
if (uname(&uts) < 0) { get_kernel_version(&major, &minor, &patch);
LOG_FATAL(LOGF_SysLinux, "Cannot uname(2) to get kernel version, sorry.");
}
patch = 0;
if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) < 2) {
LOG_FATAL(LOGF_SysLinux, "Cannot read information from uname, sorry");
}
DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch); DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) { if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
@@ -452,8 +462,8 @@ SYS_Linux_EnableSystemCallFilter(int level)
{ {
const int syscalls[] = { const int syscalls[] = {
/* Clock */ /* Clock */
SCMP_SYS(adjtimex), SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday), SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday),
SCMP_SYS(time), SCMP_SYS(settimeofday), SCMP_SYS(time),
/* Process */ /* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getrlimit), 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(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigprocmask),
@@ -463,17 +473,17 @@ SYS_Linux_EnableSystemCallFilter(int level)
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt), SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
/* Filesystem */ /* Filesystem */
SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32), SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64),
SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
SCMP_SYS(unlink), SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
/* Socket */ /* Socket */
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
SCMP_SYS(recvfrom), SCMP_SYS(recvmsg), SCMP_SYS(sendmmsg), SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
SCMP_SYS(sendmsg), SCMP_SYS(sendto), SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
/* TODO: check socketcall arguments */ /* TODO: check socketcall arguments */
SCMP_SYS(socketcall), SCMP_SYS(socketcall),
/* General I/O */ /* General I/O */
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(pipe), 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(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
SCMP_SYS(set_robust_list), SCMP_SYS(write), SCMP_SYS(set_robust_list), SCMP_SYS(write),
/* Miscellaneous */ /* Miscellaneous */
@@ -493,14 +503,17 @@ SYS_Linux_EnableSystemCallFilter(int level)
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO }, { SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif #endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR }, { SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
{ SOL_SOCKET, SO_TIMESTAMP }, { SOL_SOCKET, SO_TIMESTAMP }, { SOL_SOCKET, SO_TIMESTAMPNS },
#ifdef HAVE_LINUX_TIMESTAMPING
{ SOL_SOCKET, SO_SELECT_ERR_QUEUE }, { SOL_SOCKET, SO_TIMESTAMPING },
#endif
}; };
const static int fcntls[] = { F_GETFD, F_SETFD }; const static int fcntls[] = { F_GETFD, F_SETFD };
const static unsigned long ioctls[] = { const static unsigned long ioctls[] = {
FIONREAD, TCGETS, FIONREAD, TCGETS,
#ifdef FEAT_PPS #if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_SYS_OFFSET, PTP_SYS_OFFSET,
#endif #endif
#ifdef FEAT_PPS #ifdef FEAT_PPS
@@ -508,6 +521,9 @@ SYS_Linux_EnableSystemCallFilter(int level)
#endif #endif
#ifdef FEAT_RTC #ifdef FEAT_RTC
RTC_RD_TIME, RTC_SET_TIME, RTC_UIE_ON, RTC_UIE_OFF, RTC_RD_TIME, RTC_SET_TIME, RTC_UIE_ON, RTC_UIE_OFF,
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
SIOCETHTOOL,
#endif #endif
}; };
@@ -633,3 +649,15 @@ void SYS_Linux_MemLockAll(int LockAll)
} }
} }
#endif /* HAVE_MLOCKALL */ #endif /* HAVE_MLOCKALL */
/* ================================================== */
int
SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
{
int major, minor, patch;
get_kernel_version(&major, &minor, &patch);
return kernelvercmp(req_major, req_minor, 0, major, minor, patch) <= 0;
}

View File

@@ -39,4 +39,6 @@ extern void SYS_Linux_MemLockAll(int LockAll);
extern void SYS_Linux_SetScheduler(int SchedPriority); extern void SYS_Linux_SetScheduler(int SchedPriority);
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
#endif /* GOT_SYS_LINUX_H */ #endif /* GOT_SYS_LINUX_H */

View File

@@ -23,7 +23,7 @@
======================================================================= =======================================================================
Driver file for the MacOS X operating system. Driver file for the macOS operating system.
*/ */
@@ -39,6 +39,7 @@
#include "sys_macosx.h" #include "sys_macosx.h"
#include "conf.h" #include "conf.h"
#include "local.h"
#include "localp.h" #include "localp.h"
#include "logging.h" #include "logging.h"
#include "sched.h" #include "sched.h"
@@ -49,13 +50,13 @@
/* This register contains the number of seconds by which the local /* This register contains the number of seconds by which the local
clock was estimated to be fast of reference time at the epoch when clock was estimated to be fast of reference time at the epoch when
gettimeofday() returned T0 */ LCL_ReadRawTime() returned T0 */
static double offset_register; static double offset_register;
/* This register contains the epoch to which the offset is referenced */ /* This register contains the epoch to which the offset is referenced */
static struct timeval T0; static struct timespec T0;
/* This register contains the current estimate of the system /* This register contains the current estimate of the system
frequency, in absolute (NOT ppm) */ frequency, in absolute (NOT ppm) */
@@ -79,7 +80,7 @@ static double adjustment_requested;
static double drift_removal_interval; static double drift_removal_interval;
static double current_drift_removal_interval; static double current_drift_removal_interval;
static struct timeval Tdrift; static struct timespec Tdrift;
/* weighting applied to error in calculating drift_removal_interval */ /* weighting applied to error in calculating drift_removal_interval */
#define ERROR_WEIGHT (0.5) #define ERROR_WEIGHT (0.5)
@@ -91,7 +92,7 @@ static struct timeval Tdrift;
/* RTC synchronisation - once an hour */ /* RTC synchronisation - once an hour */
static struct timeval last_rtc_sync; static struct timespec last_rtc_sync;
#define RTC_SYNC_INTERVAL (60 * 60.0) #define RTC_SYNC_INTERVAL (60 * 60.0)
/* ================================================== */ /* ================================================== */
@@ -107,9 +108,7 @@ clock_initialise(void)
drift_removal_interval = DRIFT_REMOVAL_INTERVAL; drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
current_drift_removal_interval = DRIFT_REMOVAL_INTERVAL; current_drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
if (gettimeofday(&T0, NULL) < 0) { LCL_ReadRawTime(&T0);
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
Tdrift = T0; Tdrift = T0;
last_rtc_sync = T0; last_rtc_sync = T0;
@@ -135,21 +134,19 @@ static void
start_adjust(void) start_adjust(void)
{ {
struct timeval newadj, oldadj; struct timeval newadj, oldadj;
struct timeval T1; struct timespec T1;
double elapsed, accrued_error, predicted_error, drift_removal_elapsed; double elapsed, accrued_error, predicted_error, drift_removal_elapsed;
double adjust_required; double adjust_required;
double rounding_error; double rounding_error;
double old_adjust_remaining; double old_adjust_remaining;
/* Determine the amount of error built up since the last adjustment */ /* Determine the amount of error built up since the last adjustment */
if (gettimeofday(&T1, NULL) < 0) { LCL_ReadRawTime(&T1);
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0); elapsed = UTI_DiffTimespecsToDouble(&T1, &T0);
accrued_error = elapsed * current_freq; accrued_error = elapsed * current_freq;
UTI_DiffTimevalsToDouble(&drift_removal_elapsed, &T1, &Tdrift); drift_removal_elapsed = UTI_DiffTimespecsToDouble(&T1, &Tdrift);
/* To allow for the clock being stepped either forward or backwards, clamp /* To allow for the clock being stepped either forward or backwards, clamp
the elapsed time to bounds [ 0.0, current_drift_removal_interval ] */ the elapsed time to bounds [ 0.0, current_drift_removal_interval ] */
@@ -165,14 +162,14 @@ start_adjust(void)
adjust_required = - (accrued_error + offset_register + predicted_error); adjust_required = - (accrued_error + offset_register + predicted_error);
UTI_DoubleToTimeval(adjust_required, &newadj); UTI_DoubleToTimeval(adjust_required, &newadj);
UTI_TimevalToDouble(&newadj, &adjustment_requested); adjustment_requested = UTI_TimevalToDouble(&newadj);
rounding_error = adjust_required - adjustment_requested; rounding_error = adjust_required - adjustment_requested;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) { if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
} }
UTI_TimevalToDouble(&oldadj, &old_adjust_remaining); old_adjust_remaining = UTI_TimevalToDouble(&oldadj);
offset_register = rounding_error - old_adjust_remaining - predicted_error; offset_register = rounding_error - old_adjust_remaining - predicted_error;
@@ -184,7 +181,7 @@ start_adjust(void)
static void static void
stop_adjust(void) stop_adjust(void)
{ {
struct timeval T1; struct timespec T1;
struct timeval zeroadj, remadj; struct timeval zeroadj, remadj;
double adjustment_remaining, adjustment_achieved; double adjustment_remaining, adjustment_achieved;
double elapsed, elapsed_plus_adjust; double elapsed, elapsed_plus_adjust;
@@ -196,12 +193,10 @@ stop_adjust(void)
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
} }
if (gettimeofday(&T1, NULL) < 0) { LCL_ReadRawTime(&T1);
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0); elapsed = UTI_DiffTimespecsToDouble(&T1, &T0);
UTI_TimevalToDouble(&remadj, &adjustment_remaining); adjustment_remaining = UTI_TimevalToDouble(&remadj);
adjustment_achieved = adjustment_requested - adjustment_remaining; adjustment_achieved = adjustment_requested - adjustment_remaining;
elapsed_plus_adjust = elapsed - adjustment_achieved; elapsed_plus_adjust = elapsed - adjustment_achieved;
@@ -233,22 +228,22 @@ accrue_offset(double offset, double corr_rate)
static int static int
apply_step_offset(double offset) apply_step_offset(double offset)
{ {
struct timeval old_time, new_time, T1; struct timespec old_time, new_time, T1;
struct timeval new_time_tv;
stop_adjust(); stop_adjust();
if (gettimeofday(&old_time, NULL) < 0) { LCL_ReadRawTime(&old_time);
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time, NULL) < 0) { if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed"); DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
return 0; return 0;
} }
UTI_AddDoubleToTimeval(&T0, -offset, &T1); UTI_AddDoubleToTimespec(&T0, -offset, &T1);
T0 = T1; T0 = T1;
start_adjust(); start_adjust();
@@ -279,7 +274,7 @@ read_frequency(void)
/* ================================================== */ /* ================================================== */
static void static void
get_offset_correction(struct timeval *raw, get_offset_correction(struct timespec *raw,
double *corr, double *err) double *corr, double *err)
{ {
stop_adjust(); stop_adjust();
@@ -311,9 +306,7 @@ drift_removal_timeout(SCH_ArbitraryArgument not_used)
stop_adjust(); stop_adjust();
if (gettimeofday(&Tdrift, NULL) < 0) { LCL_ReadRawTime(&Tdrift);
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
current_drift_removal_interval = drift_removal_interval; current_drift_removal_interval = drift_removal_interval;
@@ -336,11 +329,11 @@ set_sync_status(int synchronised, double est_error, double max_error)
drift_removal_interval = MAX(drift_removal_interval, DRIFT_REMOVAL_INTERVAL); drift_removal_interval = MAX(drift_removal_interval, DRIFT_REMOVAL_INTERVAL);
} else { } else {
if (CNF_GetRtcSync()) { if (CNF_GetRtcSync()) {
struct timeval now; struct timespec now;
double rtc_sync_elapsed; double rtc_sync_elapsed;
SCH_GetLastEventTime(NULL, NULL, &now); SCH_GetLastEventTime(NULL, NULL, &now);
UTI_DiffTimevalsToDouble(&rtc_sync_elapsed, &now, &last_rtc_sync); rtc_sync_elapsed = UTI_DiffTimespecsToDouble(&now, &last_rtc_sync);
if (fabs(rtc_sync_elapsed) >= RTC_SYNC_INTERVAL) { if (fabs(rtc_sync_elapsed) >= RTC_SYNC_INTERVAL) {
/* update the RTC by applying a step of 0.0 secs */ /* update the RTC by applying a step of 0.0 secs */
apply_step_offset(0.0); apply_step_offset(0.0);

View File

@@ -23,7 +23,7 @@
======================================================================= =======================================================================
Header file for MacOS X driver Header file for macOS driver
*/ */

View File

@@ -60,6 +60,7 @@ static void
accrue_offset(double offset, double corr_rate) accrue_offset(double offset, double corr_rate)
{ {
struct timeval newadj, oldadj; struct timeval newadj, oldadj;
double doldadj;
UTI_DoubleToTimeval(-offset, &newadj); UTI_DoubleToTimeval(-offset, &newadj);
@@ -67,9 +68,9 @@ accrue_offset(double offset, double corr_rate)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
/* Add the old remaining adjustment if not zero */ /* Add the old remaining adjustment if not zero */
UTI_TimevalToDouble(&oldadj, &offset); doldadj = UTI_TimevalToDouble(&oldadj);
if (offset != 0.0) { if (doldadj != 0.0) {
UTI_AddDoubleToTimeval(&newadj, offset, &newadj); UTI_DoubleToTimeval(-offset + doldadj, &newadj);
if (PRV_AdjustTime(&newadj, NULL) < 0) if (PRV_AdjustTime(&newadj, NULL) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
} }
@@ -78,7 +79,7 @@ accrue_offset(double offset, double corr_rate)
/* ================================================== */ /* ================================================== */
static void static void
get_offset_correction(struct timeval *raw, get_offset_correction(struct timespec *raw,
double *corr, double *err) double *corr, double *err)
{ {
struct timeval remadj; struct timeval remadj;
@@ -87,7 +88,7 @@ get_offset_correction(struct timeval *raw,
if (PRV_AdjustTime(NULL, &remadj) < 0) if (PRV_AdjustTime(NULL, &remadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
UTI_TimevalToDouble(&remadj, &adjustment_remaining); adjustment_remaining = UTI_TimevalToDouble(&remadj);
*corr = adjustment_remaining; *corr = adjustment_remaining;
if (err) { if (err) {

View File

@@ -95,7 +95,7 @@ read_timeout(void *arg)
DEBUG_LOG(LOGF_TempComp, "tempcomp updated to %f for %f", comp, temp); DEBUG_LOG(LOGF_TempComp, "tempcomp updated to %f for %f", comp, temp);
if (logfileid != -1) { if (logfileid != -1) {
struct timeval now; struct timespec now;
LCL_ReadCookedTime(&now, NULL); LCL_ReadCookedTime(&now, NULL);
LOG_FileWrite(logfileid, "%s %11.4e %11.4e", LOG_FileWrite(logfileid, "%s %11.4e %11.4e",
@@ -135,7 +135,7 @@ read_points(const char *filename)
while (fgets(line, sizeof (line), f)) { while (fgets(line, sizeof (line), f)) {
p = (struct Point *)ARR_GetNewElement(points); p = (struct Point *)ARR_GetNewElement(points);
if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) { if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) {
LOG_FATAL(LOGF_Configure, "Could not read tempcomp point from %s", filename); LOG_FATAL(LOGF_TempComp, "Could not read tempcomp point from %s", filename);
break; break;
} }
} }
@@ -143,7 +143,7 @@ read_points(const char *filename)
fclose(f); fclose(f);
if (ARR_GetSize(points) < 2) if (ARR_GetSize(points) < 2)
LOG_FATAL(LOGF_Configure, "Not enough points in %s", filename); LOG_FATAL(LOGF_TempComp, "Not enough points in %s", filename);
} }
void void

View File

@@ -6,6 +6,7 @@ cd ../..
for opts in \ for opts in \
"--enable-debug" \ "--enable-debug" \
"--enable-ntp-signd" \
"--enable-scfilter" \ "--enable-scfilter" \
"--disable-asyncdns" \ "--disable-asyncdns" \
"--disable-ipv6" \ "--disable-ipv6" \
@@ -16,6 +17,8 @@ for opts in \
"--disable-cmdmon" \ "--disable-cmdmon" \
"--disable-ntp" \ "--disable-ntp" \
"--disable-refclock" \ "--disable-refclock" \
"--disable-timestamping" \
"--disable-timestamping --disable-ntp" \
"--disable-cmdmon --disable-ntp" \ "--disable-cmdmon --disable-ntp" \
"--disable-cmdmon --disable-refclock" \ "--disable-cmdmon --disable-refclock" \
"--disable-cmdmon --disable-ntp --disable-refclock" "--disable-cmdmon --disable-ntp --disable-refclock"

14
test/compilation/002-scanbuild Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
cd ../..
for opts in \
"--host-system=Linux" \
"--host-system=NetBSD" \
"--host-system=FreeBSD" \
"--without-nss" \
"--without-tomcrypt --without-nss"
do
./configure $opts
scan-build make "$@" || exit 1
done

17
test/simulation/010-multrecv Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
. ./test.common
export CLKNETSIM_RECV_MULTIPLY=4
test_start "multiple received packets"
limit=50000
client_server_options="minpoll 6 maxpoll 6"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
test_pass

18
test/simulation/011-asymjitter Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
. ./test.common
test_start "asymmetric jitter"
jitter=5e-4
jitter_asymmetry=0.47
limit=100000
max_sync_time=2000
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
test_pass

View File

@@ -37,13 +37,15 @@ for hash in $hashes; do
echo "$key" >> tmp/client.keys echo "$key" >> tmp/client.keys
done done
for key in $(seq $keys); do for version in 3 4; do
client_server_options="key $key" for key in $(seq $keys); do
run_test || test_fail client_server_options="version $version key $key"
check_chronyd_exit || test_fail run_test || test_fail
check_source_selection || test_fail check_chronyd_exit || test_fail
check_packet_interval || test_fail check_source_selection || test_fail
check_sync || test_fail check_packet_interval || test_fail
check_sync || test_fail
done
done done
server_conf="" server_conf=""

View File

@@ -11,7 +11,7 @@ sourcestats"
run_test || test_fail run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
check_chronyc_output "^Reference ID : 192\.168\.123\.1 \(192\.168\.123\.1\) check_chronyc_output "^Reference ID : C0A87B01 \(192\.168\.123\.1\)
Stratum : 2 Stratum : 2
Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010 Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time System time : 0\.0000..... seconds (slow|fast) of NTP time

View File

@@ -7,6 +7,7 @@ export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
leap=$[2 * 24 * 3600] leap=$[2 * 24 * 3600]
limit=$[4 * 24 * 3600] limit=$[4 * 24 * 3600]
client_start=$[2 * 3600]
server_conf="refclock SHM 0 dpoll 10 poll 10 server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC" leapsectz right/UTC"
refclock_jitter=1e-9 refclock_jitter=1e-9
@@ -27,14 +28,25 @@ for leapmode in system step slew; do
check_sync || test_fail check_sync || test_fail
done done
client_server_options="trust"
client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_options=""
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
for smoothmode in "" "leaponly"; do for smoothmode in "" "leaponly"; do
server_conf="refclock SHM 0 dpoll 10 poll 10 server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC leapsectz right/UTC
leapsecmode slew leapsecmode slew
smoothtime 400 0.001 $smoothmode" smoothtime 400 0.001 $smoothmode"
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
run_test || test_fail run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail

View File

@@ -3,7 +3,7 @@
. ./test.common . ./test.common
test_start "presend option" test_start "presend option"
min_sync_time=140 min_sync_time=136
max_sync_time=260 max_sync_time=260
client_server_options="presend 6 maxdelay 16" client_server_options="presend 6 maxdelay 16"
client_conf="maxdistance 10" client_conf="maxdistance 10"

View File

@@ -19,7 +19,8 @@ refclock_offset="(* 10.0 (equal 0.1 (max (sum 1.0) 1000) 1000))"
server_step="(* -10.0 (equal 0.1 (sum 1.0) 1))" server_step="(* -10.0 (equal 0.1 (sum 1.0) 1))"
server_strata=1 server_strata=1
server_conf="refclock SHM 0 dpoll 4 poll 6 server_conf="refclock SHM 0 dpoll 4 poll 6
smoothtime 2000 1" smoothtime 2000 1
maxjitter 10.0"
time_offset=-10 time_offset=-10
client_server_options="minpoll 6 maxpoll 6" client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100" client_conf="corrtimeratio 100"

36
test/simulation/122-xleave Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/bash
. ./test.common
test_start "interleaved mode"
client_server_options="xleave"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
clients=2
peers=2
max_sync_time=500
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 2) (equal 0.1 to 1)))"
client_lpeer_options="xleave minpoll 5 maxpoll 5"
run_test || test_fail
check_chronyd_exit || test_fail
# These checks are expected to fail
check_source_selection && test_fail
check_sync && test_fail
for rpoll in 4 5 6; do
client_rpeer_options="xleave minpoll $rpoll maxpoll $rpoll"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
done
test_pass

View File

@@ -31,6 +31,7 @@ default_time_offset=1e-1
default_freq_offset=1e-4 default_freq_offset=1e-4
default_base_delay=1e-4 default_base_delay=1e-4
default_jitter=1e-4 default_jitter=1e-4
default_jitter_asymmetry=0.0
default_wander=1e-9 default_wander=1e-9
default_refclock_jitter="" default_refclock_jitter=""
default_refclock_offset=0.0 default_refclock_offset=0.0
@@ -154,7 +155,16 @@ get_wander_expr() {
get_delay_expr() { get_delay_expr() {
echo "(+ $base_delay (* $jitter (exponential)))" local direction=$1 asym
if [ $jitter_asymmetry == "0.0" ]; then
asym=""
elif [ $direction = "up" ]; then
asym=$(awk "BEGIN {print 1 - 2 * $jitter_asymmetry}")
elif [ $direction = "down" ]; then
asym=$(awk "BEGIN {print 1 + 2 * $jitter_asymmetry}")
fi
echo "(+ $base_delay (* $asym $jitter (exponential)))"
} }
get_refclock_expr() { get_refclock_expr() {
@@ -378,8 +388,8 @@ run_test() {
echo "node${i}_shift_pll = $shift_pll" echo "node${i}_shift_pll = $shift_pll"
for j in $(seq 1 $nodes); do for j in $(seq 1 $nodes); do
[ $i -eq $j ] && continue [ $i -eq $j ] && continue
echo "node${i}_delay${j} = $(get_delay_expr)" echo "node${i}_delay${j} = $(get_delay_expr up)"
echo "node${j}_delay${i} = $(get_delay_expr)" echo "node${j}_delay${i} = $(get_delay_expr down)"
done done
done > tmp/conf done > tmp/conf

View File

@@ -25,7 +25,7 @@ void
test_unit(void) test_unit(void)
{ {
int i, j, index; int i, j, index;
struct timeval tv; struct timespec ts;
IPAddr ip; IPAddr ip;
char conf[][100] = { char conf[][100] = {
"clientloglimit 10000", "clientloglimit 10000",
@@ -44,33 +44,33 @@ test_unit(void)
for (i = 0; i < 500; i++) { for (i = 0; i < 500; i++) {
DEBUG_LOG(0, "iteration %d", i); DEBUG_LOG(0, "iteration %d", i);
tv.tv_sec = (time_t)random() & 0x0fffffff; ts.tv_sec = (time_t)random() & 0x0fffffff;
tv.tv_usec = 0; ts.tv_nsec = 0;
for (j = 0; j < 1000; j++) { for (j = 0; j < 1000; j++) {
TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9); TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
DEBUG_LOG(0, "address %s", UTI_IPToString(&ip)); DEBUG_LOG(0, "address %s", UTI_IPToString(&ip));
if (random() % 2) { if (random() % 2) {
index = CLG_LogNTPAccess(&ip, &tv); index = CLG_LogNTPAccess(&ip, &ts);
TEST_CHECK(index >= 0); TEST_CHECK(index >= 0);
CLG_LimitNTPResponseRate(index); CLG_LimitNTPResponseRate(index);
} else { } else {
index = CLG_LogCommandAccess(&ip, &tv); index = CLG_LogCommandAccess(&ip, &ts);
TEST_CHECK(index >= 0); TEST_CHECK(index >= 0);
CLG_LimitCommandResponseRate(index); CLG_LimitCommandResponseRate(index);
} }
UTI_AddDoubleToTimeval(&tv, (1 << random() % 14) / 100.0, &tv); UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts);
} }
} }
DEBUG_LOG(0, "records %d", ARR_GetSize(records)); DEBUG_LOG(0, "records %d", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 128); TEST_CHECK(ARR_GetSize(records) == 64);
for (i = j = 0; i < 10000; i++) { for (i = j = 0; i < 10000; i++) {
tv.tv_sec += 1; ts.tv_sec += 1;
index = CLG_LogNTPAccess(&ip, &tv); index = CLG_LogNTPAccess(&ip, &ts);
TEST_CHECK(index >= 0); TEST_CHECK(index >= 0);
if (!CLG_LimitNTPResponseRate(index)) if (!CLG_LimitNTPResponseRate(index))
j++; j++;

72
test/unit/hwclock.c Normal file
View File

@@ -0,0 +1,72 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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.
*
**********************************************************************
*/
#include <hwclock.c>
#include "test.h"
void
test_unit(void)
{
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
HCL_Instance clock;
double freq, jitter, interval, d;
int i, j;
LCL_Initialise();
clock = HCL_CreateInstance();
for (i = 0; i < 2000; i++) {
UTI_ZeroTimespec(&start_hw_ts);
UTI_ZeroTimespec(&start_local_ts);
UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts);
UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts);
DEBUG_LOG(0, "iteration %d", i);
freq = TST_GetRandomDouble(0.9, 1.1);
jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
interval = TST_GetRandomDouble(MIN_SAMPLE_SEPARATION / 10, MIN_SAMPLE_SEPARATION * 10.0);
clock->n_samples = 0;
clock->valid_coefs = 0;
for (j = 0; j < 100; j++) {
UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
UTI_AddDoubleToTimespec(&start_local_ts, j * interval, &local_ts);
if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
d = UTI_DiffTimespecsToDouble(&ts, &local_ts);
TEST_CHECK(fabs(d) <= 5.0 * jitter);
}
if (HCL_NeedsNewSample(clock, &local_ts))
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
TEST_CHECK(j < 20 || clock->valid_coefs);
if (!clock->valid_coefs)
continue;
TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
}
}
LCL_Finalise();
}

63
test/unit/smooth.c Normal file
View File

@@ -0,0 +1,63 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 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
* 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.
*
**********************************************************************
*/
#include <smooth.c>
#include "test.h"
void
test_unit(void)
{
int i, j;
struct timespec ts;
double offset, freq, wander;
char conf[] = "smoothtime 300 0.01";
CNF_Initialise(0);
CNF_ParseLine(NULL, 1, conf);
LCL_Initialise();
SMT_Initialise();
locked = 0;
for (i = 0; i < 500; i++) {
UTI_ZeroTimespec(&ts);
SMT_Reset(&ts);
DEBUG_LOG(0, "iteration %d", i);
offset = (random() % 1000000 - 500000) / 1.0e6;
freq = (random() % 1000000 - 500000) / 1.0e9;
update_smoothing(&ts, offset, freq);
for (j = 0; j < 10000; j++) {
update_smoothing(&ts, 0.0, 0.0);
UTI_AddDoubleToTimespec(&ts, 16.0, &ts);
get_smoothing(&ts, &offset, &freq, &wander);
}
TEST_CHECK(fabs(offset) < 1e-12);
TEST_CHECK(fabs(freq) < 1e-12);
TEST_CHECK(fabs(wander) < 1e-12);
}
SMT_Finalise();
LCL_Finalise();
CNF_Finalise();
}

View File

@@ -29,7 +29,7 @@ test_unit(void)
IPAddr addr; IPAddr addr;
int i, j, k, l, samples, sel_options; int i, j, k, l, samples, sel_options;
double offset, delay, disp; double offset, delay, disp;
struct timeval tv; struct timespec ts;
CNF_Initialise(0); CNF_Initialise(0);
LCL_Initialise(); LCL_Initialise();
@@ -61,8 +61,8 @@ test_unit(void)
offset = TST_GetRandomDouble(-1.0, 1.0); offset = TST_GetRandomDouble(-1.0, 1.0);
for (k = 0; k < samples; k++) { for (k = 0; k < samples; k++) {
SCH_GetLastEventTime(&tv, NULL, NULL); SCH_GetLastEventTime(&ts, NULL, NULL);
UTI_AddDoubleToTimeval(&tv, TST_GetRandomDouble(k - samples, k - samples + 1), &tv); UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(k - samples, k - samples + 1), &ts);
offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2); offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
delay = TST_GetRandomDouble(1.0e-6, 1.0e-1); delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
@@ -71,7 +71,7 @@ test_unit(void)
DEBUG_LOG(0, "source %d sample %d offset %f delay %f disp %f", j, k, DEBUG_LOG(0, "source %d sample %d offset %f delay %f disp %f", j, k,
offset, delay, disp); offset, delay, disp);
SRC_AccumulateSample(srcs[j], &tv, offset, delay, disp, delay, disp, SRC_AccumulateSample(srcs[j], &ts, offset, delay, disp, delay, disp,
1, LEAP_Normal); 1, LEAP_Normal);
} }
@@ -124,7 +124,7 @@ test_unit(void)
} }
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) { for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
SRC_ReportSource(j, &report, &tv); SRC_ReportSource(j, &report, &ts);
SRC_DestroyInstance(srcs[j]); SRC_DestroyInstance(srcs[j]);
} }
} }

View File

@@ -150,7 +150,7 @@ apply_step_offset(double offset)
} }
static void static void
offset_convert(struct timeval *raw, double *corr, double *err) offset_convert(struct timespec *raw, double *corr, double *err)
{ {
*corr = 0.0; *corr = 0.0;
if (err) if (err)

140
test/unit/util.c Normal file
View File

@@ -0,0 +1,140 @@
#include <util.c>
#include "test.h"
void test_unit(void) {
NTP_int64 ntp_ts, ntp_fuzz;
struct timespec ts, ts2;
double x, y;
Float f;
int i, j, c;
char buf[16];
for (i = -31; i < 31; i++) {
x = pow(2.0, i);
y = UTI_Log2ToDouble(i);
TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
}
for (i = -89; i < 63; i++) {
x = pow(2.0, i);
y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
}
for (i = 0; i < 100000; i++) {
x = TST_GetRandomDouble(-1000.0, 1000.0);
y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
UTI_GetRandomBytes(&f, sizeof (f));
x = UTI_FloatNetworkToHost(f);
TEST_CHECK(x > 0.0 || x <= 0.0);
}
TEST_CHECK(UTI_DoubleToNtp32(1.0) == htonl(65536));
TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0));
TEST_CHECK(UTI_DoubleToNtp32(1.0 / (65536.0)) == htonl(1));
TEST_CHECK(UTI_DoubleToNtp32(1.000001 / (65536.0)) == htonl(2));
TEST_CHECK(UTI_DoubleToNtp32(1.000001) == htonl(65537));
TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff));
TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0));
ntp_ts.hi = htonl(JAN_1970);
ntp_ts.lo = 0xffffffff;
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
TEST_CHECK(ts.tv_sec == 1);
TEST_CHECK(ts.tv_nsec == 0);
ntp_fuzz.hi = 0;
ntp_fuzz.lo = htonl(0xff1234ff);
UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
TEST_CHECK(ntp_ts.hi == htonl(JAN_1970 + 1));
TEST_CHECK(ntp_ts.lo == ntp_fuzz.lo);
ts.tv_sec = ts.tv_nsec = 0;
UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
TEST_CHECK(ntp_ts.hi == 0);
TEST_CHECK(ntp_ts.lo == 0);
TEST_CHECK(UTI_IsZeroTimespec(&ts));
TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
ts.tv_sec = 1;
ntp_ts.hi = htonl(1);
TEST_CHECK(!UTI_IsZeroTimespec(&ts));
TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
ts.tv_sec = 0;
ntp_ts.hi = 0;
ts.tv_nsec = 1;
ntp_ts.lo = htonl(1);
TEST_CHECK(!UTI_IsZeroTimespec(&ts));
TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
ntp_fuzz.hi = htonl(1);
ntp_fuzz.lo = htonl(3);
ntp_ts.hi = htonl(1);
ntp_ts.lo = htonl(2);
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
ntp_ts.hi = htonl(0x80000002);
ntp_ts.lo = htonl(2);
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
ntp_fuzz.hi = htonl(0x90000001);
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
ts.tv_sec = 1;
ts.tv_nsec = 2;
ts2.tv_sec = 1;
ts2.tv_nsec = 3;
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
ts2.tv_sec = 2;
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
for (i = -32; i <= 32; i++) {
for (j = c = 0; j < 1000; j++) {
UTI_GetNtp64Fuzz(&ntp_fuzz, i);
if (i <= 0)
TEST_CHECK(ntp_fuzz.hi == 0);
if (i < 0)
TEST_CHECK(ntohl(ntp_fuzz.lo) < 1U << (32 + i));
else if (i < 32)
TEST_CHECK(ntohl(ntp_fuzz.hi) < 1U << i);
if (ntohl(ntp_fuzz.lo) >= 1U << (31 + CLAMP(-31, i, 0)))
c++;
}
if (i == -32)
TEST_CHECK(c == 0);
else
TEST_CHECK(c > 400 && c < 600);
}
for (i = c = 0; i < 100000; i++) {
j = random() % (sizeof (buf) + 1);
UTI_GetRandomBytes(buf, j);
if (j && buf[j - 1] % 2)
c++;
}
TEST_CHECK(c > 46000 && c < 48000);
}

384
util.c
View File

@@ -34,13 +34,84 @@
#include "util.h" #include "util.h"
#include "hash.h" #include "hash.h"
#define NSEC_PER_SEC 1000000000
/* ================================================== */ /* ================================================== */
void void
UTI_TimevalToDouble(struct timeval *a, double *b) UTI_ZeroTimespec(struct timespec *ts)
{ {
*b = (double)(a->tv_sec) + 1.0e-6 * (double)(a->tv_usec); ts->tv_sec = 0;
ts->tv_nsec = 0;
}
/* ================================================== */
int
UTI_IsZeroTimespec(struct timespec *ts)
{
return !ts->tv_sec && !ts->tv_nsec;
}
/* ================================================== */
void
UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts)
{
ts->tv_sec = tv->tv_sec;
ts->tv_nsec = 1000 * tv->tv_usec;
}
/* ================================================== */
void
UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv)
{
tv->tv_sec = ts->tv_sec;
tv->tv_usec = ts->tv_nsec / 1000;
}
/* ================================================== */
double
UTI_TimespecToDouble(struct timespec *ts)
{
return ts->tv_sec + 1.0e-9 * ts->tv_nsec;
}
/* ================================================== */
void
UTI_DoubleToTimespec(double d, struct timespec *ts)
{
ts->tv_sec = d;
ts->tv_nsec = 1.0e9 * (d - ts->tv_sec);
UTI_NormaliseTimespec(ts);
}
/* ================================================== */
void
UTI_NormaliseTimespec(struct timespec *ts)
{
if (ts->tv_nsec >= NSEC_PER_SEC || ts->tv_nsec < 0) {
ts->tv_sec += ts->tv_nsec / NSEC_PER_SEC;
ts->tv_nsec = ts->tv_nsec % NSEC_PER_SEC;
/* If seconds are negative nanoseconds would end up negative too */
if (ts->tv_nsec < 0) {
ts->tv_sec--;
ts->tv_nsec += NSEC_PER_SEC;
}
}
}
/* ================================================== */
double
UTI_TimevalToDouble(struct timeval *tv)
{
return tv->tv_sec + 1.0e-6 * tv->tv_usec;
} }
/* ================================================== */ /* ================================================== */
@@ -60,26 +131,6 @@ UTI_DoubleToTimeval(double a, struct timeval *b)
/* ================================================== */ /* ================================================== */
int
UTI_CompareTimevals(struct timeval *a, struct timeval *b)
{
if (a->tv_sec < b->tv_sec) {
return -1;
} else if (a->tv_sec > b->tv_sec) {
return +1;
} else {
if (a->tv_usec < b->tv_usec) {
return -1;
} else if (a->tv_usec > b->tv_usec) {
return +1;
} else {
return 0;
}
}
}
/* ================================================== */
void void
UTI_NormaliseTimeval(struct timeval *x) UTI_NormaliseTimeval(struct timeval *x)
{ {
@@ -99,100 +150,73 @@ UTI_NormaliseTimeval(struct timeval *x)
/* ================================================== */ /* ================================================== */
void int
UTI_DiffTimevals(struct timeval *result, UTI_CompareTimespecs(struct timespec *a, struct timespec *b)
struct timeval *a,
struct timeval *b)
{ {
result->tv_sec = a->tv_sec - b->tv_sec; if (a->tv_sec < b->tv_sec)
result->tv_usec = a->tv_usec - b->tv_usec; return -1;
if (a->tv_sec > b->tv_sec)
return 1;
if (a->tv_nsec < b->tv_nsec)
return -1;
if (a->tv_nsec > b->tv_nsec)
return 1;
return 0;
}
/* Correct microseconds field to bring it into the range /* ================================================== */
(0,1000000) */
UTI_NormaliseTimeval(result); /* JGH */ void
UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b)
{
result->tv_sec = a->tv_sec - b->tv_sec;
result->tv_nsec = a->tv_nsec - b->tv_nsec;
UTI_NormaliseTimespec(result);
} }
/* ================================================== */ /* ================================================== */
/* Calculate result = a - b and return as a double */ /* Calculate result = a - b and return as a double */
void double
UTI_DiffTimevalsToDouble(double *result, UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b)
struct timeval *a,
struct timeval *b)
{ {
*result = (double)(a->tv_sec - b->tv_sec) + return (a->tv_sec - b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
(double)(a->tv_usec - b->tv_usec) * 1.0e-6;
} }
/* ================================================== */ /* ================================================== */
void void
UTI_AddDoubleToTimeval(struct timeval *start, UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end)
double increment,
struct timeval *end)
{ {
long int_part, frac_part; time_t int_part;
/* Don't want to do this by using (long)(1000000 * increment), since int_part = increment;
that will only cope with increments up to +/- 2148 seconds, which end->tv_sec = start->tv_sec + int_part;
is too marginal here. */ end->tv_nsec = start->tv_nsec + 1.0e9 * (increment - int_part);
UTI_NormaliseTimespec(end);
int_part = (long) increment;
increment = (increment - int_part) * 1.0e6;
frac_part = (long) (increment > 0.0 ? increment + 0.5 : increment - 0.5);
end->tv_sec = int_part + start->tv_sec;
end->tv_usec = frac_part + start->tv_usec;
UTI_NormaliseTimeval(end);
} }
/* ================================================== */ /* ================================================== */
/* Calculate the average and difference (as a double) of two timevals */ /* Calculate the average and difference (as a double) of two timespecs */
void void
UTI_AverageDiffTimevals (struct timeval *earlier, UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later,
struct timeval *later, struct timespec *average, double *diff)
struct timeval *average,
double *diff)
{ {
struct timeval tvdiff; *diff = UTI_DiffTimespecsToDouble(later, earlier);
struct timeval tvhalf; UTI_AddDoubleToTimespec(earlier, *diff / 2.0, average);
}
UTI_DiffTimevals(&tvdiff, later, earlier);
*diff = (double)tvdiff.tv_sec + 1.0e-6 * (double)tvdiff.tv_usec;
if (*diff < 0.0) {
/* Either there's a bug elsewhere causing 'earlier' and 'later' to
be backwards, or something wierd has happened. Maybe when we
change the frequency on Linux? */
/* Assume the required behaviour is to treat it as zero */
*diff = 0.0;
}
tvhalf.tv_sec = tvdiff.tv_sec / 2;
tvhalf.tv_usec = tvdiff.tv_usec / 2 + (tvdiff.tv_sec % 2) * 500000; /* JGH */
average->tv_sec = earlier->tv_sec + tvhalf.tv_sec;
average->tv_usec = earlier->tv_usec + tvhalf.tv_usec;
/* Bring into range */
UTI_NormaliseTimeval(average);
}
/* ================================================== */ /* ================================================== */
void void
UTI_AddDiffToTimeval(struct timeval *a, struct timeval *b, UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b,
struct timeval *c, struct timeval *result) struct timespec *c, struct timespec *result)
{ {
double diff; double diff;
UTI_DiffTimevalsToDouble(&diff, a, b); diff = UTI_DiffTimespecsToDouble(a, b);
UTI_AddDoubleToTimeval(c, diff, result); UTI_AddDoubleToTimespec(c, diff, result);
} }
/* ================================================== */ /* ================================================== */
@@ -205,21 +229,20 @@ static int pool_ptr = 0;
#define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)]) #define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)])
/* ================================================== */ /* ================================================== */
/* Convert a timeval into a temporary string, largely for diagnostic /* Convert a timespec into a temporary string, largely for diagnostic display */
display */
char * char *
UTI_TimevalToString(struct timeval *tv) UTI_TimespecToString(struct timespec *ts)
{ {
char *result; char *result;
result = NEXT_BUFFER; result = NEXT_BUFFER;
#ifdef HAVE_LONG_TIME_T #ifdef HAVE_LONG_TIME_T
snprintf(result, BUFFER_LENGTH, "%"PRId64".%06lu", snprintf(result, BUFFER_LENGTH, "%"PRId64".%09lu",
(int64_t)tv->tv_sec, (unsigned long)tv->tv_usec); (int64_t)ts->tv_sec, (unsigned long)ts->tv_nsec);
#else #else
snprintf(result, BUFFER_LENGTH, "%ld.%06lu", snprintf(result, BUFFER_LENGTH, "%ld.%09lu",
(long)tv->tv_sec, (unsigned long)tv->tv_usec); (long)ts->tv_sec, (unsigned long)ts->tv_nsec);
#endif #endif
return result; return result;
} }
@@ -229,11 +252,11 @@ UTI_TimevalToString(struct timeval *tv)
for diagnostic display */ for diagnostic display */
char * char *
UTI_TimestampToString(NTP_int64 *ts) UTI_Ntp64ToString(NTP_int64 *ntp_ts)
{ {
struct timeval tv; struct timespec ts;
UTI_Int64ToTimeval(ts, &tv); UTI_Ntp64ToTimespec(ntp_ts, &ts);
return UTI_TimevalToString(&tv); return UTI_TimespecToString(&ts);
} }
/* ================================================== */ /* ================================================== */
@@ -408,6 +431,8 @@ UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest)
case IPADDR_INET6: case IPADDR_INET6:
memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6)); memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
break; break;
default:
dest->family = htons(IPADDR_UNSPEC);
} }
} }
@@ -425,6 +450,8 @@ UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest)
case IPADDR_INET6: case IPADDR_INET6:
memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6)); memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
break; break;
default:
dest->family = IPADDR_UNSPEC;
} }
} }
@@ -589,19 +616,19 @@ UTI_TimeToLogForm(time_t t)
/* ================================================== */ /* ================================================== */
void void
UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double *delta_time, double dfreq, double doffset) UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
{ {
double elapsed; double elapsed;
UTI_DiffTimevalsToDouble(&elapsed, when, old_tv); elapsed = UTI_DiffTimespecsToDouble(when, old_ts);
*delta_time = elapsed * dfreq - doffset; *delta_time = elapsed * dfreq - doffset;
UTI_AddDoubleToTimeval(old_tv, *delta_time, new_tv); UTI_AddDoubleToTimespec(old_ts, *delta_time, new_ts);
} }
/* ================================================== */ /* ================================================== */
void void
UTI_GetInt64Fuzz(NTP_int64 *ts, int precision) UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision)
{ {
int start, bits; int start, bits;
@@ -620,7 +647,7 @@ UTI_GetInt64Fuzz(NTP_int64 *ts, int precision)
/* ================================================== */ /* ================================================== */
double double
UTI_Int32ToDouble(NTP_int32 x) UTI_Ntp32ToDouble(NTP_int32 x)
{ {
return (double) ntohl(x) / 65536.0; return (double) ntohl(x) / 65536.0;
} }
@@ -630,39 +657,84 @@ UTI_Int32ToDouble(NTP_int32 x)
#define MAX_NTP_INT32 (4294967295.0 / 65536.0) #define MAX_NTP_INT32 (4294967295.0 / 65536.0)
NTP_int32 NTP_int32
UTI_DoubleToInt32(double x) UTI_DoubleToNtp32(double x)
{ {
if (x > MAX_NTP_INT32) NTP_int32 r;
x = MAX_NTP_INT32;
else if (x < 0) if (x >= MAX_NTP_INT32) {
x = 0.0; r = 0xffffffff;
return htonl((NTP_int32)(0.5 + 65536.0 * x)); } else if (x <= 0.0) {
r = 0;
} else {
x *= 65536.0;
r = x;
/* Round up */
if (r < x)
r++;
}
return htonl(r);
} }
/* ================================================== */ /* ================================================== */
/* Seconds part of NTP timestamp correponding to the origin of the void
struct timeval format. */ UTI_ZeroNtp64(NTP_int64 *ts)
{
ts->hi = ts->lo = htonl(0);
}
/* ================================================== */
int
UTI_IsZeroNtp64(NTP_int64 *ts)
{
return !ts->hi && !ts->lo;
}
/* ================================================== */
int
UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
{
int32_t diff;
if (a->hi == b->hi && a->lo == b->lo)
return 0;
diff = ntohl(a->hi) - ntohl(b->hi);
if (diff < 0)
return -1;
if (diff > 0)
return 1;
return ntohl(a->lo) < ntohl(b->lo) ? -1 : 1;
}
/* ================================================== */
/* Seconds part of NTP timestamp correponding to the origin of the time_t format */
#define JAN_1970 0x83aa7e80UL #define JAN_1970 0x83aa7e80UL
#define NSEC_PER_NTP64 4.294967296
void void
UTI_TimevalToInt64(struct timeval *src, UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz)
NTP_int64 *dest, NTP_int64 *fuzz)
{ {
uint32_t hi, lo, sec, usec; uint32_t hi, lo, sec, nsec;
sec = (uint32_t)src->tv_sec; sec = (uint32_t)src->tv_sec;
usec = (uint32_t)src->tv_usec; nsec = (uint32_t)src->tv_nsec;
/* Recognize zero as a special case - it always signifies /* Recognize zero as a special case - it always signifies
an 'unknown' value */ an 'unknown' value */
if (!usec && !sec) { if (!nsec && !sec) {
hi = lo = 0; hi = lo = 0;
} else { } else {
hi = htonl(sec + JAN_1970); hi = htonl(sec + JAN_1970);
lo = htonl(NSEC_PER_NTP64 * nsec);
/* This formula gives an error of about 0.1us worst case */
lo = htonl(4295 * usec - (usec >> 5) - (usec >> 9));
/* Add the fuzz */ /* Add the fuzz */
if (fuzz) { if (fuzz) {
@@ -678,8 +750,7 @@ UTI_TimevalToInt64(struct timeval *src,
/* ================================================== */ /* ================================================== */
void void
UTI_Int64ToTimeval(NTP_int64 *src, UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
struct timeval *dest)
{ {
uint32_t ntp_sec, ntp_frac; uint32_t ntp_sec, ntp_frac;
@@ -696,8 +767,9 @@ UTI_Int64ToTimeval(NTP_int64 *src,
dest->tv_sec = ntp_sec - JAN_1970; dest->tv_sec = ntp_sec - JAN_1970;
#endif #endif
/* Until I invent a slick way to do this, just do it the obvious way */ dest->tv_nsec = ntp_frac / NSEC_PER_NTP64 + 0.5;
dest->tv_usec = (int)(0.5 + (double)(ntp_frac) / 4294.967296);
UTI_NormaliseTimespec(dest);
} }
/* ================================================== */ /* ================================================== */
@@ -709,7 +781,7 @@ UTI_Int64ToTimeval(NTP_int64 *src,
#define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600) #define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600)
int int
UTI_IsTimeOffsetSane(struct timeval *tv, double offset) UTI_IsTimeOffsetSane(struct timespec *ts, double offset)
{ {
double t; double t;
@@ -717,8 +789,7 @@ UTI_IsTimeOffsetSane(struct timeval *tv, double offset)
if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET)) if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET))
return 0; return 0;
UTI_TimevalToDouble(tv, &t); t = UTI_TimespecToDouble(ts) + offset;
t += offset;
/* Time before 1970 is not considered valid */ /* Time before 1970 is not considered valid */
if (t < 0.0) if (t < 0.0)
@@ -756,14 +827,14 @@ UTI_Log2ToDouble(int l)
/* ================================================== */ /* ================================================== */
void void
UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest) UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
{ {
uint32_t sec_low; uint32_t sec_low;
#ifdef HAVE_LONG_TIME_T #ifdef HAVE_LONG_TIME_T
uint32_t sec_high; uint32_t sec_high;
#endif #endif
dest->tv_usec = ntohl(src->tv_nsec) / 1000; dest->tv_nsec = ntohl(src->tv_nsec);
sec_low = ntohl(src->tv_sec_low); sec_low = ntohl(src->tv_sec_low);
#ifdef HAVE_LONG_TIME_T #ifdef HAVE_LONG_TIME_T
sec_high = ntohl(src->tv_sec_high); sec_high = ntohl(src->tv_sec_high);
@@ -774,14 +845,16 @@ UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest)
#else #else
dest->tv_sec = sec_low; dest->tv_sec = sec_low;
#endif #endif
UTI_NormaliseTimespec(dest);
} }
/* ================================================== */ /* ================================================== */
void void
UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest) UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest)
{ {
dest->tv_nsec = htonl(src->tv_usec * 1000); dest->tv_nsec = htonl(src->tv_nsec);
#ifdef HAVE_LONG_TIME_T #ifdef HAVE_LONG_TIME_T
dest->tv_sec_high = htonl((uint64_t)src->tv_sec >> 32); dest->tv_sec_high = htonl((uint64_t)src->tv_sec >> 32);
#else #else
@@ -894,57 +967,6 @@ UTI_FdSetCloexec(int fd)
/* ================================================== */ /* ================================================== */
int
UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len, unsigned char *auth, int auth_len)
{
return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
}
/* ================================================== */
int
UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len, const unsigned char *auth, int auth_len)
{
unsigned char buf[MAX_HASH_LENGTH];
return UTI_GenerateNTPAuth(hash_id, key, key_len, data, data_len,
buf, sizeof (buf)) == auth_len && !memcmp(buf, auth, auth_len);
}
/* ================================================== */
int
UTI_DecodePasswordFromText(char *key)
{
int i, j, len = strlen(key);
char buf[3], *p;
if (!strncmp(key, "ASCII:", 6)) {
memmove(key, key + 6, len - 6);
return len - 6;
} else if (!strncmp(key, "HEX:", 4)) {
if ((len - 4) % 2)
return 0;
for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
key[i] = strtol(buf, &p, 16);
if (p != buf + 2)
return 0;
}
return i;
} else {
/* assume ASCII */
return len;
}
}
/* ================================================== */
int int
UTI_SetQuitSignalsHandler(void (*handler)(int)) UTI_SetQuitSignalsHandler(void (*handler)(int))
{ {

91
util.h
View File

@@ -34,45 +34,68 @@
#include "candm.h" #include "candm.h"
#include "hash.h" #include "hash.h"
/* Zero a timespec */
extern void UTI_ZeroTimespec(struct timespec *ts);
/* Check if a timespec is zero */
extern int UTI_IsZeroTimespec(struct timespec *ts);
/* Convert a timeval into a timespec */
extern void UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts);
/* Convert a timespec into a timeval */
extern void UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv);
/* Convert a timespec into a floating point number of seconds */
extern double UTI_TimespecToDouble(struct timespec *ts);
/* Convert a number of seconds expressed in floating point into a
timespec */
extern void UTI_DoubleToTimespec(double d, struct timespec *ts);
/* Normalise a timespec, by adding or subtracting seconds to bring
its nanosecond field into range */
extern void UTI_NormaliseTimespec(struct timespec *ts);
/* Convert a timeval into a floating point number of seconds */ /* Convert a timeval into a floating point number of seconds */
extern void UTI_TimevalToDouble(struct timeval *a, double *b); extern double UTI_TimevalToDouble(struct timeval *tv);
/* Convert a number of seconds expressed in floating point into a /* Convert a number of seconds expressed in floating point into a
timeval */ timeval */
extern void UTI_DoubleToTimeval(double a, struct timeval *b); extern void UTI_DoubleToTimeval(double a, struct timeval *b);
/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
and +1 if a comes after b */
extern int UTI_CompareTimevals(struct timeval *a, struct timeval *b);
/* Normalise a struct timeval, by adding or subtracting seconds to bring /* Normalise a struct timeval, by adding or subtracting seconds to bring
its microseconds field into range */ its microseconds field into range */
extern void UTI_NormaliseTimeval(struct timeval *x); extern void UTI_NormaliseTimeval(struct timeval *x);
/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
and +1 if a comes after b */
extern int UTI_CompareTimespecs(struct timespec *a, struct timespec *b);
/* Calculate result = a - b */ /* Calculate result = a - b */
extern void UTI_DiffTimevals(struct timeval *result, struct timeval *a, struct timeval *b); extern void UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b);
/* Calculate result = a - b and return as a double */ /* Calculate result = a - b and return as a double */
extern void UTI_DiffTimevalsToDouble(double *result, struct timeval *a, struct timeval *b); extern double UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b);
/* Add a double increment to a timeval to get a new one. 'start' is /* Add a double increment to a timespec to get a new one. 'start' is
the starting time, 'end' is the result that we return. This is the starting time, 'end' is the result that we return. This is
safe to use if start and end are the same */ safe to use if start and end are the same */
extern void UTI_AddDoubleToTimeval(struct timeval *start, double increment, struct timeval *end); extern void UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end);
/* Calculate the average and difference (as a double) of two timevals */ /* Calculate the average and difference (as a double) of two timespecs */
extern void UTI_AverageDiffTimevals(struct timeval *earlier, struct timeval *later, struct timeval *average, double *diff); extern void UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later, struct timespec *average, double *diff);
/* Calculate result = a - b + c */ /* Calculate result = a - b + c */
extern void UTI_AddDiffToTimeval(struct timeval *a, struct timeval *b, struct timeval *c, struct timeval *result); extern void UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b, struct timespec *c, struct timespec *result);
/* Convert a timeval into a temporary string, largely for diagnostic /* Convert a timespec into a temporary string, largely for diagnostic
display */ display */
extern char *UTI_TimevalToString(struct timeval *tv); extern char *UTI_TimespecToString(struct timespec *ts);
/* Convert an NTP timestamp into a temporary string, largely for /* Convert an NTP timestamp into a temporary string, largely for
diagnostic display */ diagnostic display */
extern char *UTI_TimestampToString(NTP_int64 *ts); extern char *UTI_Ntp64ToString(NTP_int64 *ts);
/* Convert ref_id into a temporary string, for diagnostics */ /* Convert ref_id into a temporary string, for diagnostics */
extern char *UTI_RefidToString(uint32_t ref_id); extern char *UTI_RefidToString(uint32_t ref_id);
@@ -95,26 +118,38 @@ extern const char *UTI_SockaddrFamilyToString(int family);
extern char *UTI_TimeToLogForm(time_t t); extern char *UTI_TimeToLogForm(time_t t);
/* Adjust time following a frequency/offset change */ /* Adjust time following a frequency/offset change */
extern void UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double *delta, double dfreq, double doffset); extern void UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset);
/* Get zero NTP timestamp with random bits below precision */ /* Get zero NTP timestamp with random bits below precision */
extern void UTI_GetInt64Fuzz(NTP_int64 *ts, int precision); extern void UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision);
extern double UTI_Int32ToDouble(NTP_int32 x); extern double UTI_Ntp32ToDouble(NTP_int32 x);
extern NTP_int32 UTI_DoubleToInt32(double x); extern NTP_int32 UTI_DoubleToNtp32(double x);
extern void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest, NTP_int64 *fuzz); /* Zero an NTP timestamp */
extern void UTI_ZeroNtp64(NTP_int64 *ts);
extern void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest); /* Check if an NTP timestamp is zero */
extern int UTI_IsZeroNtp64(NTP_int64 *ts);
/* Compare two NTP timestamps. Returns -1 if a is before b, 0 if a is equal to
b, and 1 if a is after b. */
extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b);
/* Convert a timespec into an NTP timestamp */
extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz);
/* Convert an NTP timestamp into a timespec */
extern void UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest);
/* Check if time + offset is sane */ /* Check if time + offset is sane */
extern int UTI_IsTimeOffsetSane(struct timeval *tv, double offset); extern int UTI_IsTimeOffsetSane(struct timespec *ts, double offset);
/* Get 2 raised to power of a signed integer */ /* Get 2 raised to power of a signed integer */
extern double UTI_Log2ToDouble(int l); extern double UTI_Log2ToDouble(int l);
extern void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest); extern void UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest);
extern void UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest); extern void UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest);
extern double UTI_FloatNetworkToHost(Float x); extern double UTI_FloatNetworkToHost(Float x);
extern Float UTI_FloatHostToNetwork(double x); extern Float UTI_FloatHostToNetwork(double x);
@@ -122,14 +157,6 @@ extern Float UTI_FloatHostToNetwork(double x);
/* Set FD_CLOEXEC on descriptor */ /* Set FD_CLOEXEC on descriptor */
extern int UTI_FdSetCloexec(int fd); extern int UTI_FdSetCloexec(int fd);
extern int UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len, unsigned char *auth, int auth_len);
extern int UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len, const unsigned char *auth, int auth_len);
/* Decode password encoded in ASCII or HEX */
extern int UTI_DecodePasswordFromText(char *key);
extern int UTI_SetQuitSignalsHandler(void (*handler)(int)); extern int UTI_SetQuitSignalsHandler(void (*handler)(int));
/* Get directory (as an allocated string) for a path */ /* Get directory (as an allocated string) for a path */