Compare commits

...

100 Commits

Author SHA1 Message Date
Miroslav Lichvar
d1777087c1 doc: update NEWS 2018-08-31 10:11:17 +02:00
Miroslav Lichvar
cf7b5363cd test: extend 110-chronyc test 2018-08-31 09:55:43 +02:00
Miroslav Lichvar
7f3183cc72 test: extend 106-refclock test 2018-08-31 09:55:43 +02:00
Miroslav Lichvar
f1b8da085b doc: update FAQ 2018-08-30 11:56:13 +02:00
Miroslav Lichvar
09dfca49ec configure: fix detection of timepps.h on NetBSD
The header requires <time.h> for struct timespec.
2018-08-30 11:56:13 +02:00
Miroslav Lichvar
88e0ec07aa refclock: fix compiler warning on FreeBSD 2018-08-30 11:56:13 +02:00
Miroslav Lichvar
0adc8e8f92 ntp: add support for IP_RECVDSTADDR and IP_SENDSRCADDR
FreeBSD doesn't support IP_PKTINFO. Instead it provides IP_RECVDSTADDR
and IP_SENDSRCADDR, which can be used to get/set the destination/source
address.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The sys/capability.h header from libcap-2.16 and earlier disables the
linux/types.h header, which breaks the linux/ptp_clock.h header. Change
the order to include sys/capability.h as the last system header.
2018-04-05 16:18:23 +02:00
82 changed files with 2179 additions and 1142 deletions

View File

@@ -36,7 +36,7 @@ DESTDIR=
HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \
smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@

25
NEWS
View File

@@ -1,3 +1,28 @@
New in version 3.4
==================
Enhancements
------------
* Add filter option to server/pool/peer directive
* Add minsamples and maxsamples options to hwtimestamp directive
* Add support for faster frequency adjustments in Linux 4.19
* Change default pidfile to /var/run/chrony/chronyd.pid to allow
chronyd without root privileges to remove it on exit
* Disable sub-second polling intervals for distant NTP sources
* Extend range of supported sub-second polling intervals
* Get/set IPv4 destination/source address of NTP packets on FreeBSD
* Make burst options and command useful with short polling intervals
* Modify auto_offline option to activate when sending request failed
* Respond from interface that received NTP request if possible
* Add onoffline command to switch between online and offline state
according to current system network configuration
* Improve example NetworkManager dispatcher script
Bug fixes
---------
* Avoid waiting in Linux getrandom system call
* Fix PPS support on FreeBSD and NetBSD
New in version 3.3
==================

View File

@@ -100,7 +100,8 @@
#define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61
#define REQ_SHUTDOWN 62
#define N_REQUEST_TYPES 63
#define REQ_ONOFFLINE 63
#define N_REQUEST_TYPES 64
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -275,7 +276,8 @@ typedef struct {
Float asymmetry;
Float offset;
uint32_t flags;
uint32_t reserved[4];
int32_t filter_length;
uint32_t reserved[3];
int32_t EOR;
} REQ_NTP_Source;

View File

@@ -111,7 +111,7 @@ read_line(void)
char *cmd;
rl_attempted_completion_function = command_name_completion;
rl_basic_word_break_characters = "\t\n\r";
rl_basic_word_break_characters = " \t\n\r";
/* save line only if not empty */
cmd = readline(prompt);
@@ -426,6 +426,14 @@ process_cmd_online(CMD_Request *msg, char *line)
/* ================================================== */
static void
process_cmd_onoffline(CMD_Request *msg, char *line)
{
msg->command = htons(REQ_ONOFFLINE);
}
/* ================================================== */
static int
read_address_integer(char *line, IPAddr *address, int *value)
{
@@ -1105,7 +1113,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry);
msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
msg->data.ntp_source.flags = htonl(
(data.params.online ? REQ_ADDSRC_ONLINE : 0) |
(data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) |
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
@@ -1114,6 +1122,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
result = 1;
@@ -1208,6 +1217,8 @@ give_help(void)
"minstratum <address> <stratum>\0Modify minimum stratum\0"
"offline [<mask>/<address>]\0Set sources in subnet to offline status\0"
"online [<mask>/<address>]\0Set sources in subnet to online status\0"
"onoffline\0Set all sources to online or offline status\0"
"\0according to network configuration\0"
"polltarget <address> <target>\0Modify poll target\0"
"refresh\0Refresh IP addresses\0"
"\0\0"
@@ -1270,30 +1281,52 @@ give_help(void)
/* Tab-completion when editline/readline is available */
#ifdef FEAT_READLINE
enum {
TAB_COMPLETE_BASE_CMDS,
TAB_COMPLETE_ADD_OPTS,
TAB_COMPLETE_MANUAL_OPTS,
TAB_COMPLETE_SOURCES_OPTS,
TAB_COMPLETE_SOURCESTATS_OPTS,
TAB_COMPLETE_MAX_INDEX
};
static int tab_complete_index;
static char *
command_name_generator(const char *text, int state)
{
const char *name, *names[] = {
"accheck", "activity", "add peer", "add server", "allow", "burst",
const char *name, **names[TAB_COMPLETE_MAX_INDEX];
const char *base_commands[] = {
"accheck", "activity", "add", "allow", "burst",
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
"manual on", "manual off", "manual delete", "manual list", "manual reset",
"maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
"retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
"smoothtime", "sources", "sources -v", "sourcestats", "sourcestats -v",
"smoothtime", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
const char *add_options[] = { "peer", "server", NULL };
const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
const char *sources_options[] = { "-v", NULL };
const char *sourcestats_options[] = { "-v", NULL };
static int list_index, len;
names[TAB_COMPLETE_BASE_CMDS] = base_commands;
names[TAB_COMPLETE_ADD_OPTS] = add_options;
names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
names[TAB_COMPLETE_SOURCES_OPTS] = sources_options;
names[TAB_COMPLETE_SOURCESTATS_OPTS] = sourcestats_options;
if (!state) {
list_index = 0;
len = strlen(text);
}
while ((name = names[list_index++])) {
while ((name = names[tab_complete_index][list_index++])) {
if (strncmp(name, text, len) == 0) {
return strdup(name);
}
@@ -1307,7 +1340,25 @@ command_name_generator(const char *text, int state)
static char **
command_name_completion(const char *text, int start, int end)
{
char first[32];
snprintf(first, MIN(sizeof (first), start + 1), "%s", rl_line_buffer);
rl_attempted_completion_over = 1;
if (!strcmp(first, "add ")) {
tab_complete_index = TAB_COMPLETE_ADD_OPTS;
} else if (!strcmp(first, "manual ")) {
tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
} else if (!strcmp(first, "sources ")) {
tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
} else if (!strcmp(first, "sourcestats ")) {
tab_complete_index = TAB_COMPLETE_SOURCESTATS_OPTS;
} else if (first[0] == '\0') {
tab_complete_index = TAB_COMPLETE_BASE_CMDS;
} else {
return NULL;
}
return rl_completion_matches(text, command_name_generator);
}
#endif
@@ -1588,17 +1639,17 @@ print_seconds(unsigned long s)
if (s == (uint32_t)-1) {
printf(" -");
} else if (s < 1200) {
printf("%4ld", s);
printf("%4lu", s);
} else if (s < 36000) {
printf("%3ldm", s / 60);
printf("%3lum", s / 60);
} else if (s < 345600) {
printf("%3ldh", s / 3600);
printf("%3luh", s / 3600);
} else {
d = s / 86400;
if (d > 999) {
printf("%3ldy", d / 365);
printf("%3luy", d / 365);
} else {
printf("%3ldd", d);
printf("%3lud", d);
}
}
}
@@ -2840,7 +2891,7 @@ process_cmd_keygen(char *line)
snprintf(hash_name, sizeof (hash_name), "MD5");
#endif
if (sscanf(line, "%u %16s %d", &id, hash_name, &bits))
if (sscanf(line, "%u %16s %u", &id, hash_name, &bits))
;
length = CLAMP(10, (bits + 7) / 8, sizeof (key));
@@ -2984,6 +3035,8 @@ process_line(char *line)
do_normal_submit = process_cmd_offline(&tx_message, line);
} else if (!strcmp(command, "online")) {
do_normal_submit = process_cmd_online(&tx_message, line);
} else if (!strcmp(command, "onoffline")) {
process_cmd_onoffline(&tx_message, line);
} else if (!strcmp(command, "polltarget")) {
do_normal_submit = process_cmd_polltarget(&tx_message, line);
} else if (!strcmp(command, "quit")) {
@@ -3206,7 +3259,7 @@ main(int argc, char **argv)
hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1";
}
UTI_SetQuitSignalsHandler(signal_handler);
UTI_SetQuitSignalsHandler(signal_handler, 0);
sockaddrs = get_sockaddrs(hostnames, port);

View File

@@ -139,6 +139,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* ADD_SERVER3 */
PERMIT_AUTH, /* ADD_PEER3 */
PERMIT_AUTH, /* SHUTDOWN */
PERMIT_AUTH, /* ONOFFLINE */
};
/* ================================================== */
@@ -424,7 +425,7 @@ handle_online(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.online.address, &address);
if (!NSR_TakeSourcesOnline(&mask, &address))
if (!NSR_SetConnectivity(&mask, &address, SRC_ONLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
@@ -437,12 +438,24 @@ handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.offline.address, &address);
if (!NSR_TakeSourcesOffline(&mask, &address))
if (!NSR_SetConnectivity(&mask, &address, SRC_OFFLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_onoffline(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address, mask;
address.family = mask.family = IPADDR_UNSPEC;
if (!NSR_SetConnectivity(&mask, &address, SRC_MAYBE_ONLINE))
;
}
/* ================================================== */
static void
handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message)
{
@@ -787,6 +800,7 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio =
@@ -797,7 +811,8 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
@@ -1636,6 +1651,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_shutdown(&rx_message, &tx_message);
break;
case REQ_ONOFFLINE:
handle_onoffline(&rx_message, &tx_message);
break;
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);

View File

@@ -48,7 +48,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
src->params.online = 1;
src->params.connectivity = SRC_ONLINE;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.burst = 0;
@@ -59,6 +59,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.authkey = INACTIVE_AUTHKEY;
@@ -90,7 +91,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0;
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
@@ -106,6 +107,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;

12
conf.c
View File

@@ -1291,6 +1291,8 @@ parse_hwtimestamp(char *line)
iface = ARR_GetNewElement(hwts_interfaces);
iface->name = Strdup(p);
iface->minpoll = 0;
iface->min_samples = 2;
iface->max_samples = 16;
iface->nocrossts = 0;
iface->rxfilter = CNF_HWTS_RXFILTER_ANY;
iface->precision = 100.0e-9;
@@ -1300,9 +1302,15 @@ parse_hwtimestamp(char *line)
for (p = line; *p; line += n, p = line) {
line = CPS_SplitWord(line);
if (!strcasecmp(p, "minpoll")) {
if (!strcasecmp(p, "maxsamples")) {
if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
break;
} else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
} else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
break;
} else if (!strcasecmp(p, "precision")) {
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
break;
@@ -1411,7 +1419,7 @@ CNF_AddInitSources(void)
ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i);
ntp_addr.port = cps_source.port;
cps_source.params.iburst = 1;
cps_source.params.online = 0;
cps_source.params.connectivity = SRC_OFFLINE;
NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params);
}

2
conf.h
View File

@@ -128,6 +128,8 @@ typedef enum {
typedef struct {
char *name;
int minpoll;
int min_samples;
int max_samples;
int nocrossts;
CNF_HwTs_RxFilter rxfilter;
double precision;

18
configure vendored
View File

@@ -108,7 +108,7 @@ For better control, use the options below.
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support
@@ -229,7 +229,7 @@ feat_ntp_signd=0
ntp_era_split=""
default_user="root"
default_hwclockfile=""
default_pidfile="/var/run/chronyd.pid"
default_pidfile="/var/run/chrony/chronyd.pid"
default_rtcdevice="/dev/rtc"
mail_program="/usr/lib/sendmail"
@@ -595,14 +595,6 @@ else
fi
fi
if test_code '<stdint.h>' 'stdint.h' '' '' ''; then
add_def HAVE_STDINT_H
fi
if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
add_def HAVE_INTTYPES_H
fi
if test_code 'struct in_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
struct in_pktinfo ipi;
return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;'
@@ -715,11 +707,11 @@ fi
timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
if test_code '<sys/timepps.h>' 'inttypes.h time.h sys/timepps.h' '' '' ''; then
timepps_h="sys/timepps.h"
add_def HAVE_SYS_TIMEPPS_H
else
if test_code '<timepps.h>' 'timepps.h' '' '' ''; then
if test_code '<timepps.h>' 'inttypes.h time.h timepps.h' '' '' ''; then
timepps_h="timepps.h"
add_def HAVE_TIMEPPS_H
fi
@@ -727,7 +719,7 @@ if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
fi
if [ "x$timepps_h" != "x" ] && \
test_code 'PPSAPI' "string.h $timepps_h" '' '' '
test_code 'PPSAPI' "inttypes.h string.h time.h $timepps_h" '' '' '
pps_handle_t h = 0;
pps_info_t i;
struct timespec ts;

View File

@@ -69,27 +69,30 @@ options:
This option specifies the minimum interval between requests sent to the server
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
polling interval should not drop below 32 seconds. The default is 6 (64
seconds), the minimum is -4 (1/16th of a second), and the maximum is 24 (6
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
months). Note that intervals shorter than 6 (64 seconds) should generally not
be used with public servers on the Internet, because it might be considered
abuse.
abuse. A sub-second interval will be enabled only when the server is reachable
and the round-trip delay is shorter than 10 milliseconds, i.e. the server
should be in a local network.
*maxpoll* _poll_:::
This option specifies the maximum interval between requests sent to the server
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
months).
*iburst*:::
With this option, the interval between the first four requests sent to the
server will be 2 seconds instead of the interval specified by the *minpoll*
option, which allows *chronyd* to make the first update of the clock shortly
after start.
server will be 2 seconds or less instead of the interval specified by the
*minpoll* option, which allows *chronyd* to make the first update of the clock
shortly after start.
*burst*:::
With this option, *chronyd* will shorten the interval between up to four
requests to 2 seconds when it cannot get a good measurement from the server.
The number of requests in the burst is limited by the current polling interval
to keep the average interval at or above the minimum interval, i.e. the current
interval needs to be at least two times longer than the minimum interval in
order to allow a burst with two requests.
requests to 2 seconds or less when it cannot get a good measurement from the
server. The number of requests in the burst is limited by the current polling
interval to keep the average interval at or above the minimum interval, i.e.
the current interval needs to be at least two times longer than the minimum
interval in order to allow a burst with two requests.
*key* _ID_:::
The NTP protocol supports a message authentication code (MAC) to prevent
computers having their system time upset by rogue packets being sent to them.
@@ -158,19 +161,23 @@ Set the minimum number of samples kept for this source. This overrides the
*maxsamples* _samples_:::
Set the maximum number of samples kept for this source. This overrides the
<<maxsamples,*maxsamples*>> directive.
*filter* _samples_:::
This option enables a median filter to reduce noise in NTP measurements. The
filter will reduce the specified number of samples to a single sample. It is
intended to be used with very short polling intervals in local networks where
it is acceptable to generate a lot of NTP traffic.
*offline*:::
If the server will not be reachable when *chronyd* is started, the *offline*
option can be specified. *chronyd* will not try to poll the server until it is
enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
*chronyc*).
*auto_offline*:::
With this option, the server will be assumed to have gone offline when two
requests have been sent to it without receiving a response. This option avoids
With this option, the server will be assumed to have gone offline when sending
a request fails, e.g. due to a missing route to the network. This option avoids
the need to run the <<chronyc.adoc#offline,*offline*>> command from *chronyc*
when disconnecting the network link, if it is safe to assume that the requests
and responses will not be dropped in the network, e.g. in a trusted local
network. (It will still be necessary to use the <<chronyc.adoc#online,*online*>>
command when the link has been established, to enable measurements to start.)
when disconnecting the network link. (It will still be necessary to use the
<<chronyc.adoc#online,*online*>> command when the link has been established, to
enable measurements to start.)
*prefer*:::
Prefer this source over sources without the *prefer* option.
*noselect*:::
@@ -1938,6 +1945,12 @@ It's defined as a power of two. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
of a second).
*minsamples* _samples_:::
This option specifies the minimum number of readings kept for tracking of the
NIC clock. The default value is 2.
*maxsamples* _samples_:::
This option specifies the maximum number of readings kept for tracking of the
NIC clock. The default value is 16.
*precision* _precision_:::
This option specifies the assumed precision of reading of the NIC clock. The
default value is 100e-9 (100 nanoseconds).
@@ -2046,10 +2059,11 @@ significant impact on performance as *chronyd's* memory usage is modest. The
*mlockall(2)* man page has more details.
[[pidfile]]*pidfile* _file_::
*chronyd* always writes its process ID (PID) to a file, and checks this file on
startup to see if another *chronyd* might already be running on the system. By
default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
allows the name to be changed, e.g.:
Unless *chronyd* is started with the *-Q* option, it writes its process ID
(PID) to a file, and checks this file on startup to see if another *chronyd*
might already be running on the system. By default, the file used is
_@DEFAULT_PID_FILE@_. The *pidfile* directive allows the name to be changed,
e.g.:
+
----
pidfile /run/chronyd.pid

View File

@@ -338,8 +338,9 @@ register has 8 bits and is updated on every received or missed packet from
the source. A value of 377 indicates that a valid reply was received for all
from the last eight transmissions.
*LastRx*:::
This column shows how long ago the last sample was received from the source.
This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
This column shows how long ago the last good sample (which is shown in the next
column) was received from the source. Measurements that failed some tests are
ignored. This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
minutes, hours, days, or years.
*Last sample*:::
This column shows the offset between the local clock and the source at the
@@ -722,6 +723,13 @@ particular source or sources has been restored.
+
The syntax is identical to that of the <<offline,*offline*>> command.
[[onoffline]]
*onoffline*::
The *onoffline* command tells *chronyd* to switch all sources to the online or
offline status according to the current network configuration. A source is
considered online if it is possible to send requests to it, i.e. a route to the
network is present.
[[polltarget]]*polltarget* _address_ _polltarget_::
The *polltarget* command is used to modify the poll target for one of the
current set of sources. It is equivalent to the *polltarget* option in the

View File

@@ -81,9 +81,9 @@ started without root privileges.
*-r*::
This option will try to reload and then delete files containing sample
histories for each of the servers and reference clocks being used. These
histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
*chronyc*, or by setting the <<chrony.conf.adoc#dumponexit,*dumponexit*>>
histories for each of the servers and reference clocks being used. The
files are expected to be in the directory specified by the
<<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock

View File

@@ -171,6 +171,11 @@ network latency and stability of the system clock (which mainly depends on the
temperature sensitivity of the crystal oscillator and the maximum rate of the
temperature change).
Generally, if the `sourcestats` command usually reports a small number of
samples retained for a source (e.g. fewer than 16), a shorter polling interval
should be considered. If the number of samples is usually at the maximum of 64,
a longer polling interval may work better.
An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
@@ -178,15 +183,15 @@ allowed to poll frequently could be
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
----
An example using very short polling intervals for a server located in the same
An example using shorter polling intervals with a server located in the same
LAN could be
----
server ntp.local minpoll 2 maxpoll 4 polltarget 30
----
The maxdelay options are useful to ignore measurements with larger delay (e.g.
due to congestion in the network) and improve the stability of the
The maxdelay options are useful to ignore measurements with an unusally large
delay (e.g. due to congestion in the network) and improve the stability of the
synchronisation. The `maxdelaydevratio` option could be added to the example
with local NTP server
@@ -194,17 +199,34 @@ with local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
If your server supports the interleaved mode, the `xleave` option should be
added to the `server` directive in order to allow the server to send the
client more accurate hardware or kernel transmit timestamps. When combined with
local hardware timestamping, sub-microsecond accuracy may be possible. An
example could be
If your server supports the interleaved mode (e.g. it is running `chronyd`),
the `xleave` option should be added to the `server` directive in order to allow
the server to send the client more accurate transmit timestamps (kernel or
preferably hardware). For example:
----
server ntp.local minpoll 2 maxpoll 2 xleave
server ntp.local minpoll 2 maxpoll 4 xleave
----
When combined with local hardware timestamping, good network switches, and even
shorter polling intervals, a sub-microsecond accuracy and stability of a few
tens of nanoseconds may be possible. For example:
----
server ntp.local minpoll 0 maxpoll 0 xleave
hwtimestamp eth0
----
If it is acceptable for NTP clients in the network to send requests at an
excessive rate, a sub-second polling interval may be specified. A median filter
can be enabled in order to update the clock at a reduced rate with more stable
measurements. For example:
----
server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
hwtimestamp eth0 minpoll -6
----
=== Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
@@ -292,6 +314,49 @@ to
makestep 1 -1
----
=== Using a Windows NTP server?
A common issue with Windows NTP servers is that they report a very large root
dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
server for being too inaccurate. The `sources` command may show a valid
measurement, but the server is not selected for synchronisation. You can check
the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
The `maxdistance` value needs to be increased in _chrony.conf_ to enable
synchronisation to such a server. For example:
----
maxdistance 16.0
----
=== Using a PPS reference clock?
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
determine which second of UTC corresponds to each pulse. If it is another
reference clock specified with the `lock` option in the `refclock` directive,
the offset between the two reference clocks must be smaller than 0.2 seconds in
order for the PPS reference clock to work. With NMEA reference clocks it is
common to have a larger offset. It needs to be corrected with the `offset`
option.
One approach to find out a good value of the `offset` option is to configure
the reference clocks with the `noselect` option and compare them to an NTP
server. For example, if the `sourcestats` command showed
----
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
foo.example.net 7 3 200 -2.991 16.141 -107us 492us
----
the offset of the NMEA source would need to be increased by about 0.504
seconds. It does not have to be very accurate. As long as the offset of the
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
able to determine the seconds corresponding to the pulses and allow the samples
to be used for synchronisation.
== Issues with `chronyc`
=== I keep getting the error `506 Cannot talk to daemon`

View File

@@ -1,37 +1,15 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
# online or offline when a network interface is configured or removed
# This is a NetworkManager dispatcher / networkd-dispatcher script for
# chronyd to set its NTP sources online or offline when a network interface
# is configured or removed
export LC_ALL=C
[ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# Check if there is a default route
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
if /sbin/ip route list 2> /dev/null | grep -q '^default'; then
chronyc online > /dev/null 2>&1
exit 0
fi
sources=$(chronyc -c -n sources 2> /dev/null)
[ $? -ne 0 ] && exit 0
# Check each configured source if it has a route
echo "$sources" | while IFS=, read mode state address rest; do
[ "$mode" != '^' ] && [ "$mode" != '=' ] && continue
/sbin/ip route get "$address" > /dev/null 2>&1 && command="online" || command="offline"
# Set priority of sources so that the selected source is set as
# last if offline to avoid unnecessary reselection
[ "$state" != '*' ] && priority=1 || priority=2
echo "$priority $command $address"
done | sort | while read priority command address; do
echo "$command $address"
done | chronyc > /dev/null 2>&1
chronyc onoffline > /dev/null 2>&1
exit 0

View File

@@ -7,7 +7,7 @@ ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
PIDFile=/var/run/chronyd.pid
PIDFile=/var/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
PrivateTmp=yes

View File

@@ -49,18 +49,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
if (out_len < 16)
return 0;
MD5Init(&ctx);
MD5Update(&ctx, in1, in1_len);
if (in2)
MD5Update(&ctx, in2, in2_len);
MD5Final(&ctx);
memcpy(out, ctx.digest, 16);
out_len = MIN(out_len, 16);
return 16;
memcpy(out, ctx.digest, out_len);
return out_len;
}
void

View File

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

View File

@@ -29,6 +29,7 @@
#include "config.h"
#include "hash.h"
#include "util.h"
struct hash {
const char *name;
@@ -105,19 +106,24 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
unsigned long len;
int r;
len = out_len;
len = sizeof (buf);
if (in2)
r = hash_memory_multi(id, out, &len,
in1, (unsigned long)in1_len, in2, (unsigned long)in2_len, NULL, 0);
r = hash_memory_multi(id, buf, &len,
in1, (unsigned long)in1_len,
in2, (unsigned long)in2_len, NULL, 0);
else
r = hash_memory(id, in1, in1_len, out, &len);
r = hash_memory(id, in1, in1_len, buf, &len);
if (r != CRYPT_OK)
return 0;
len = MIN(len, out_len);
memcpy(out, buf, len);
return len;
}

View File

@@ -36,8 +36,9 @@
#include "regress.h"
#include "util.h"
/* Maximum number of samples per clock */
#define MAX_SAMPLES 16
/* Minimum and maximum number of samples per clock */
#define MIN_SAMPLES 2
#define MAX_SAMPLES 64
/* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0)
@@ -49,10 +50,12 @@ struct HCL_Instance_Record {
/* Samples stored as intervals (uncorrected for frequency error)
relative to local_ref and hw_ref */
double x_data[MAX_SAMPLES];
double y_data[MAX_SAMPLES];
double *x_data;
double *y_data;
/* Number of samples */
/* Minimum, maximum and current number of samples */
int min_samples;
int max_samples;
int n_samples;
/* Maximum error of the last sample */
@@ -89,13 +92,21 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
HCL_Instance
HCL_CreateInstance(double min_separation)
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
{
HCL_Instance clock;
min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
max_samples = MAX(min_samples, max_samples);
clock = MallocNew(struct HCL_Instance_Record);
clock->x_data[MAX_SAMPLES - 1] = 0.0;
clock->y_data[MAX_SAMPLES - 1] = 0.0;
clock->x_data = MallocArray(double, max_samples);
clock->y_data = MallocArray(double, max_samples);
clock->x_data[max_samples - 1] = 0.0;
clock->y_data[max_samples - 1] = 0.0;
clock->min_samples = min_samples;
clock->max_samples = max_samples;
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
@@ -110,6 +121,8 @@ HCL_CreateInstance(double min_separation)
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
Free(clock->y_data);
Free(clock->x_data);
Free(clock);
}
@@ -138,7 +151,7 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
/* Shift old samples */
if (clock->n_samples) {
if (clock->n_samples >= MAX_SAMPLES)
if (clock->n_samples >= clock->max_samples)
clock->n_samples--;
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
@@ -149,7 +162,7 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
DEBUG_LOG("HW clock reset interval=%f", local_delta);
}
for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
for (i = clock->max_samples - clock->n_samples; i < clock->max_samples; i++) {
clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
clock->x_data[i - 1] = clock->x_data[i] - local_delta;
}
@@ -162,8 +175,8 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
/* Get new coefficients */
clock->valid_coefs =
RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
clock->y_data + MAX_SAMPLES - clock->n_samples,
RGR_FindBestRobustRegression(clock->x_data + clock->max_samples - clock->n_samples,
clock->y_data + clock->max_samples - clock->n_samples,
clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
&n_runs, &best_start);
@@ -175,7 +188,8 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
clock->frequency = raw_freq / local_freq;
/* Drop unneeded samples */
clock->n_samples -= best_start;
if (clock->n_samples > clock->min_samples)
clock->n_samples -= MIN(best_start, clock->n_samples - clock->min_samples);
/* If the fit doesn't cross the error interval of the last sample,
or the frequency is not sane, drop all samples and start again */

View File

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

4
keys.c
View File

@@ -107,6 +107,8 @@ determine_hash_delay(uint32_t key_id)
double diff, min_diff;
int i, nsecs;
memset(&pkt, 0, sizeof (pkt));
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH,
@@ -212,7 +214,7 @@ KEY_Reload(void)
continue;
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
LOG(LOGS_WARN, "Could not parse key at line %d in file %s", line_number, key_file);
LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
continue;
}

View File

@@ -132,14 +132,16 @@ void LOG_Message(LOG_Severity severity,
char buf[2048];
va_list other_args;
time_t t;
struct tm stm;
struct tm *tm;
if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
fprintf(file_log, "%s ", buf);
tm = gmtime(&t);
if (tm) {
strftime(buf, sizeof (buf), "%Y-%m-%dT%H:%M:%SZ", tm);
fprintf(file_log, "%s ", buf);
}
#if DEBUG > 0
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
@@ -182,13 +184,20 @@ LOG_OpenFileLog(const char *log_file)
{
FILE *f;
f = fopen(log_file, "a");
if (!f)
LOG_FATAL("Could not open log file %s", log_file);
if (log_file) {
f = fopen(log_file, "a");
if (!f)
LOG_FATAL("Could not open log file %s", log_file);
} else {
f = stderr;
}
/* Enable line buffering */
setvbuf(f, NULL, _IOLBF, BUFSIZ);
if (file_log && file_log != stderr)
fclose(file_log);
file_log = f;
}

View File

@@ -99,7 +99,7 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
*/
extern void LOG_SetDebugLevel(int level);
/* Log messages to a file instead of stderr */
/* Log messages to a file instead of stderr, or stderr again if NULL */
extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */

8
main.c
View File

@@ -530,9 +530,6 @@ int main
/* Check whether another chronyd may already be running */
check_pidfile();
/* Write our pidfile to prevent other chronyds running */
write_pidfile();
if (!user)
user = CNF_GetUser();
@@ -543,6 +540,9 @@ int main
/* Create directories for sockets, log files, and dump files */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Write our pidfile to prevent other instances from running */
write_pidfile();
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
@@ -586,7 +586,7 @@ int main
/* From now on, it is safe to do finalisation on exit */
initialised = 1;
UTI_SetQuitSignalsHandler(signal_cleanup);
UTI_SetQuitSignalsHandler(signal_cleanup, 1);
CAM_OpenUnixSocket();

View File

@@ -27,6 +27,8 @@
#ifndef GOT_MEMORY_H
#define GOT_MEMORY_H
#include "sysincl.h"
/* Wrappers checking for errors */
extern void *Malloc(size_t size);
extern void *Realloc(void *ptr, size_t size);

15
ntp.h
View File

@@ -121,4 +121,19 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Structure used to save NTP measurements. time is the local time at which
the sample is to be considered to have been made and offset is the offset at
the time (positive indicates that the local clock is slow relative to the
source). root_delay/root_dispersion include peer_delay/peer_dispersion. */
typedef struct {
struct timespec time;
double offset;
double peer_delay;
double peer_dispersion;
double root_delay;
double root_dispersion;
int stratum;
NTP_Leap leap;
} NTP_Sample;
#endif /* GOT_NTP_H */

View File

@@ -37,6 +37,7 @@
#include "sched.h"
#include "reference.h"
#include "local.h"
#include "samplefilt.h"
#include "smooth.h"
#include "sources.h"
#include "util.h"
@@ -89,8 +90,8 @@ struct NCR_Instance_Record {
int tx_suspended; /* Boolean indicating we can't transmit yet */
int auto_burst; /* If 1, initiate a burst on each poll */
int auto_offline; /* If 1, automatically go offline if server/peer
isn't responding */
int auto_offline; /* If 1, automatically go offline when requests
cannot be sent */
int local_poll; /* Log2 of polling interval at our end */
int remote_poll; /* Log2 of server/peer's polling interval (recovered
@@ -195,6 +196,9 @@ struct NCR_Instance_Record {
SRC_Instance source;
/* Optional median filter for NTP measurements */
SPF_Instance filter;
int burst_good_samples_to_go;
int burst_total_samples_to_go;
@@ -217,7 +221,7 @@ static ARR_Instance broadcasts;
/* Spacing required between samples for any two servers/peers (to
minimise risk of network collisions) (in seconds) */
#define MIN_SAMPLING_SEPARATION 0.02
#define MIN_SAMPLING_SEPARATION 0.002
#define MAX_SAMPLING_SEPARATION 0.2
/* Randomness added to spacing between samples for one server/peer */
@@ -226,12 +230,10 @@ static ARR_Instance broadcasts;
/* Adjustment of the peer polling interval */
#define PEER_SAMPLING_ADJ 1.1
/* Spacing between samples in burst mode for one server/peer */
#define BURST_INTERVAL 2.0
/* Time to wait before retransmitting in burst mode, if we did not get
a reply to the previous probe */
#define BURST_TIMEOUT 2.0
/* Maximum spacing between samples in the burst mode as an absolute
value and ratio to the normal polling interval */
#define MAX_BURST_INTERVAL 2.0
#define MAX_BURST_POLL_RATIO 0.25
/* Number of samples in initial burst */
#define IBURST_GOOD_SAMPLES 4
@@ -266,19 +268,20 @@ static ARR_Instance broadcasts;
#define MAX_MAXDELAYDEVRATIO 1.0e6
/* Minimum and maximum allowed poll interval */
#define MIN_MINPOLL -4
#define MIN_MAXPOLL 0
#define MIN_POLL -6
#define MAX_POLL 24
/* Enable sub-second polling intervals only when the peer delay is not
longer than 10 milliseconds to restrict them to local networks */
#define MIN_NONLAN_POLL 0
#define MAX_LAN_PEER_DELAY 0.01
/* Kiss-o'-Death codes */
#define KOD_RATE 0x52415445UL /* RATE */
/* Maximum poll interval set by KoD RATE */
#define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL
/* Maximum number of missed responses to follow peer's polling interval */
#define MAX_PEER_POLL_TX 8
/* Maximum number of missed responses to accept samples using old timestamps
in the interleaved client/server mode */
#define MAX_CLIENT_INTERLEAVED_TX 4
@@ -537,12 +540,13 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->interleaved = params->interleaved;
result->minpoll = params->minpoll;
if (result->minpoll < MIN_MINPOLL)
if (result->minpoll < MIN_POLL)
result->minpoll = SRC_DEFAULT_MINPOLL;
else if (result->minpoll > MAX_POLL)
result->minpoll = MAX_POLL;
result->maxpoll = params->maxpoll;
if (result->maxpoll < MIN_MAXPOLL)
if (result->maxpoll < MIN_POLL)
result->maxpoll = SRC_DEFAULT_MAXPOLL;
else if (result->maxpoll > MAX_POLL)
result->maxpoll = MAX_POLL;
@@ -600,10 +604,18 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
params->min_samples, params->max_samples,
params->min_delay, params->asymmetry);
if (params->filter_length >= 1)
result->filter = SPF_CreateInstance(params->filter_length, params->filter_length,
NTP_MAX_DISPERSION, 0.0);
else
result->filter = NULL;
result->rx_timeout_id = 0;
result->tx_timeout_id = 0;
result->tx_suspended = 1;
result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
result->opmode = params->connectivity == SRC_ONLINE ||
(params->connectivity == SRC_MAYBE_ONLINE &&
NIO_IsServerConnectable(remote_addr)) ? MD_ONLINE : MD_OFFLINE;
result->local_poll = result->minpoll;
result->poll_score = 0.0;
zero_local_timestamp(&result->local_tx);
@@ -632,6 +644,9 @@ NCR_DestroyInstance(NCR_Instance instance)
if (instance->mode == MODE_ACTIVE)
NIO_CloseServerSocket(instance->local_addr.sock_fd);
if (instance->filter)
SPF_DestroyInstance(instance->filter);
/* This will destroy the source instance inside the
structure, which will cause reselection if this was the
synchronising source etc. */
@@ -677,6 +692,9 @@ NCR_ResetInstance(NCR_Instance instance)
instance->updated_init_timestamps = 0;
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
zero_local_timestamp(&instance->init_local_rx);
if (instance->filter)
SPF_DropSamples(instance->filter);
}
/* ================================================== */
@@ -742,6 +760,13 @@ adjust_poll(NCR_Instance inst, double adj)
inst->local_poll = inst->maxpoll;
inst->poll_score = 1.0;
}
/* Don't allow a sub-second polling interval if the source is not reachable
or it is not in a local network according to the measured delay */
if (inst->local_poll < MIN_NONLAN_POLL &&
(!SRC_IsReachable(inst->source) ||
SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY))
inst->local_poll = MIN_NONLAN_POLL;
}
/* ================================================== */
@@ -753,6 +778,9 @@ get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance)
int samples;
if (error_in_estimate > peer_distance) {
/* If the prediction is not even within +/- the peer distance of the peer,
we are clearly not tracking the peer at all well, so we back off the
sampling rate depending on just how bad the situation is */
poll_adj = -log(error_in_estimate / peer_distance) / log(2.0);
} else {
samples = SST_Samples(SRC_GetSourcestats(inst->source));
@@ -782,7 +810,7 @@ get_transmit_poll(NCR_Instance inst)
/* In symmetric mode, if the peer is responding, use shorter of the local
and remote poll interval, but not shorter than the minimum */
if (inst->mode == MODE_ACTIVE && poll > inst->remote_poll &&
inst->tx_count < MAX_PEER_POLL_TX)
SRC_IsReachable(inst->source))
poll = MAX(inst->remote_poll, inst->minpoll);
return poll;
@@ -850,7 +878,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
case MD_BURST_WAS_ONLINE:
case MD_BURST_WAS_OFFLINE:
/* Burst modes */
delay_time = on_tx ? BURST_TIMEOUT : BURST_INTERVAL;
delay_time = MIN(MAX_BURST_INTERVAL, MAX_BURST_POLL_RATIO * delay_time);
break;
default:
assert(0);
@@ -868,6 +896,8 @@ get_separation(int poll)
{
double separation;
assert(poll >= MIN_POLL && poll <= MAX_POLL);
/* Allow up to 8 sources using the same short interval to not be limited
by the separation */
separation = UTI_Log2ToDouble(poll - 3);
@@ -914,7 +944,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
)
{
NTP_Packet message;
int auth_len, mac_len, length, ret, precision;
int auth_len, max_auth_len, length, ret, precision;
struct timespec local_receive, local_transmit;
double smooth_offset, local_transmit_err;
NTP_int64 ts_fuzz;
@@ -1052,24 +1082,21 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
&message.transmit_ts, &ts_fuzz);
if (auth_mode == AUTH_SYMMETRIC) {
/* Truncate long MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */
max_auth_len = version == 4 ?
NTP_MAX_V4_MAC_LENGTH - 4 : sizeof (message.auth_data);
auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
offsetof(NTP_Packet, auth_keyid),
(unsigned char *)&message.auth_data,
sizeof (message.auth_data));
(unsigned char *)&message.auth_data, max_auth_len);
if (!auth_len) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0;
}
message.auth_keyid = htonl(key_id);
mac_len = sizeof (message.auth_keyid) + auth_len;
/* Truncate MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */
if (version == 4 && mac_len > NTP_MAX_V4_MAC_LENGTH)
mac_len = NTP_MAX_V4_MAC_LENGTH;
length += mac_len;
length += sizeof (message.auth_keyid) + auth_len;
} else if (auth_mode == AUTH_MSSNTP) {
/* MS-SNTP packets are signed (asynchronously) by ntp_signd */
return NSD_SignAndSendPacket(key_id, &message, where_to, from, length);
@@ -1132,7 +1159,7 @@ transmit_timeout(void *arg)
/* Start a new burst if the burst option is enabled and the average
polling interval including the burst will not fall below the
minimum polling interval */
if (inst->auto_burst && inst->local_poll > inst->minpoll && inst->local_poll > 1)
if (inst->auto_burst && inst->local_poll > inst->minpoll)
NCR_InitiateSampleBurst(inst, BURST_GOOD_SAMPLES,
MIN(1 << (inst->local_poll - inst->minpoll),
MAX_BURST_TOTAL_SAMPLES));
@@ -1141,10 +1168,6 @@ transmit_timeout(void *arg)
break;
}
/* With auto_offline take the source offline on 2nd missed reply */
if (inst->auto_offline && inst->tx_count >= 2)
NCR_TakeSourceOffline(inst);
if (inst->opmode == MD_OFFLINE) {
return;
}
@@ -1228,6 +1251,10 @@ transmit_timeout(void *arg)
SRC_UpdateReachability(inst->source, 0);
}
/* With auto_offline take the source offline if sending failed */
if (!sent && inst->auto_offline)
NCR_SetConnectivity(inst, SRC_OFFLINE);
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
/* When not reachable, don't stop online burst until sending succeeds */
@@ -1237,6 +1264,8 @@ transmit_timeout(void *arg)
case MD_BURST_WAS_OFFLINE:
--inst->burst_total_samples_to_go;
break;
case MD_OFFLINE:
return;
default:
break;
}
@@ -1437,10 +1466,57 @@ check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
/* ================================================== */
static void
process_sample(NCR_Instance inst, NTP_Sample *sample)
{
double estimated_offset, error_in_estimate, filtered_sample_ago;
NTP_Sample filtered_sample;
int filtered_samples;
/* Accumulate the sample to the median filter if it is enabled. When the
filter produces a result, check if it is not too old, i.e. the filter did
not miss too many samples due to missing responses or failing tests. */
if (inst->filter) {
SPF_AccumulateSample(inst->filter, sample);
filtered_samples = SPF_GetNumberOfSamples(inst->filter);
if (!SPF_GetFilteredSample(inst->filter, &filtered_sample))
return;
filtered_sample_ago = UTI_DiffTimespecsToDouble(&sample->time, &filtered_sample.time);
if (filtered_sample_ago > SOURCE_REACH_BITS / 2 * filtered_samples *
UTI_Log2ToDouble(inst->local_poll)) {
DEBUG_LOG("filtered sample dropped ago=%f poll=%d", filtered_sample_ago,
inst->local_poll);
return;
}
sample = &filtered_sample;
}
/* Get the estimated offset predicted from previous samples. The
convention here is that positive means local clock FAST of
reference, i.e. backwards to the way that 'offset' is defined. */
estimated_offset = SST_PredictOffset(SRC_GetSourcestats(inst->source), &sample->time);
error_in_estimate = fabs(-sample->offset - estimated_offset);
SRC_AccumulateSample(inst->source, sample);
SRC_SelectSource(inst->source);
adjust_poll(inst, get_poll_adj(inst, error_in_estimate,
sample->peer_dispersion + 0.5 * sample->peer_delay));
}
/* ================================================== */
static int
receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
NTP_Sample sample;
SST_Stats stats;
int pkt_leap, pkt_version;
@@ -1449,24 +1525,6 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
double pkt_root_dispersion;
AuthenticationMode pkt_auth_mode;
/* The local time to which the (offset, delay, dispersion) triple will
be taken to relate. For client/server operation this is practically
the same as either the transmit or receive time. The difference comes
in symmetric active mode, when the receive may come minutes after the
transmit, and this time will be midway between the two */
struct timespec sample_time;
/* The estimated offset in seconds, a positive value indicates that the local
clock is SLOW of the remote source and a negative value indicates that the
local clock is FAST of the remote source */
double offset;
/* The estimated peer delay, dispersion and distance */
double delay, dispersion, distance;
/* The total root delay and dispersion */
double root_delay, root_dispersion;
/* The skew and estimated frequency offset relative to the remote source */
double skew, source_freq_lo, source_freq_hi;
@@ -1481,15 +1539,6 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Kiss-o'-Death codes */
int kod_rate;
/* The estimated offset predicted from previous samples. The
convention here is that positive means local clock FAST of
reference, i.e. backwards to the way that 'offset' is defined. */
double estimated_offset;
/* The absolute difference between the offset estimate and
measurement in seconds */
double error_in_estimate;
NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time;
double delay_time, precision;
@@ -1608,23 +1657,23 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
precision = LCL_GetSysPrecisionAsQuantum() + UTI_Log2ToDouble(message->precision);
/* Calculate delay */
delay = fabs(local_interval - remote_interval);
if (delay < precision)
delay = precision;
sample.peer_delay = fabs(local_interval - remote_interval);
if (sample.peer_delay < precision)
sample.peer_delay = precision;
/* Calculate offset. Following the NTP definition, this is negative
if we are fast of the remote source. */
offset = UTI_DiffTimespecsToDouble(&remote_average, &local_average);
sample.offset = UTI_DiffTimespecsToDouble(&remote_average, &local_average);
/* Apply configured correction */
offset += inst->offset_correction;
sample.offset += inst->offset_correction;
/* We treat the time of the sample as being midway through the local
measurement period. An analysis assuming constant relative
frequency and zero network delay shows this is the only possible
choice to estimate the frequency difference correctly for every
sample pair. */
sample_time = local_average;
sample.time = local_average;
SST_GetFrequencyRange(stats, &source_freq_lo, &source_freq_hi);
@@ -1632,8 +1681,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
skew = (source_freq_hi - source_freq_lo) / 2.0;
/* and then calculate peer dispersion */
dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
skew * fabs(local_interval);
sample.peer_dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
skew * fabs(local_interval);
/* If the source is an active peer, this is the minimum assumed interval
between previous two transmissions (if not constrained by minpoll) */
@@ -1647,10 +1696,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
processing time is sane, and in interleaved symmetric mode that the
measured delay and intervals between remote timestamps don't indicate
a missed response */
testA = delay - dispersion <= inst->max_delay && precision <= inst->max_delay &&
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
precision <= inst->max_delay &&
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_ACTIVE && interleaved_packet &&
(delay > 0.5 * prev_remote_poll_interval ||
(sample.peer_delay > 0.5 * prev_remote_poll_interval ||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
(inst->remote_poll <= inst->prev_local_poll &&
UTI_DiffTimespecsToDouble(&remote_transmit, &prev_remote_transmit) >
@@ -1659,14 +1709,14 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Test B requires in client mode that the ratio of the round trip delay
to the minimum one currently in the stats data register is less than an
administrator-defined value */
testB = check_delay_ratio(inst, stats, &sample_time, delay);
testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
/* Test C requires that the ratio of the increase in delay from the minimum
one in the stats data register to the standard deviation of the offsets
in the register is less than an administrator-defined value or the
difference between measured offset and predicted offset is larger than
the increase in delay */
testC = check_delay_dev_ratio(inst, stats, &sample_time, offset, delay);
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
/* Test D requires that the remote peer is not synchronised to us to
prevent a synchronisation loop */
@@ -1674,8 +1724,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
pkt_refid != UTI_IPToRefid(&local_addr->ip_addr);
} else {
remote_interval = local_interval = response_time = 0.0;
offset = delay = dispersion = 0.0;
sample_time = rx_ts->ts;
sample.offset = sample.peer_delay = sample.peer_dispersion = 0.0;
sample.time = rx_ts->ts;
local_receive = *rx_ts;
local_transmit = inst->local_tx;
testA = testB = testC = testD = 0;
@@ -1685,9 +1735,10 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
the additional tests passed */
good_packet = testA && testB && testC && testD;
root_delay = pkt_root_delay + delay;
root_dispersion = pkt_root_dispersion + dispersion;
distance = dispersion + 0.5 * delay;
sample.root_delay = pkt_root_delay + sample.peer_delay;
sample.root_dispersion = pkt_root_dispersion + sample.peer_dispersion;
sample.stratum = MAX(message->stratum, inst->min_stratum);
sample.leap = (NTP_Leap)pkt_leap;
/* Update the NTP timestamps. If it's a valid packet from a synchronised
source, the timestamps may be used later when processing a packet in the
@@ -1756,7 +1807,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_Ntp64ToString(&message->receive_ts),
UTI_Ntp64ToString(&message->transmit_ts));
DEBUG_LOG("offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f",
offset, delay, dispersion, root_delay, root_dispersion);
sample.offset, sample.peer_delay, sample.peer_dispersion,
sample.root_delay, sample.root_dispersion);
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
remote_interval, local_interval, response_time,
tss_chars[local_transmit.source], tss_chars[local_receive.source]);
@@ -1778,26 +1830,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
SRC_UpdateReachability(inst->source, synced_packet);
if (good_packet) {
/* Do this before we accumulate a new sample into the stats registers, obviously */
estimated_offset = SST_PredictOffset(stats, &sample_time);
SRC_AccumulateSample(inst->source,
&sample_time,
offset, delay, dispersion,
root_delay, root_dispersion,
MAX(message->stratum, inst->min_stratum),
(NTP_Leap) pkt_leap);
SRC_SelectSource(inst->source);
/* Now examine the registers. First though, if the prediction is
not even within +/- the peer distance of the peer, we are clearly
not tracking the peer at all well, so we back off the sampling
rate depending on just how bad the situation is. */
error_in_estimate = fabs(-offset - estimated_offset);
/* Now update the polling interval */
adjust_poll(inst, get_poll_adj(inst, error_in_estimate, distance));
/* Adjust the polling interval, accumulate the sample, etc. */
process_sample(inst, &sample);
/* If we're in burst mode, check whether the burst is completed and
revert to the previous mode */
@@ -1864,9 +1898,9 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->report.root_dispersion = pkt_root_dispersion;
inst->report.ref_id = pkt_refid;
UTI_Ntp64ToTimespec(&message->reference_ts, &inst->report.ref_time);
inst->report.offset = offset;
inst->report.peer_delay = delay;
inst->report.peer_dispersion = dispersion;
inst->report.offset = sample.offset;
inst->report.peer_delay = sample.peer_delay;
inst->report.peer_dispersion = sample.peer_dispersion;
inst->report.response_time = response_time;
inst->report.jitter_asymmetry = SST_GetJitterAsymmetry(stats);
inst->report.tests = ((((((((test1 << 1 | test2) << 1 | test3) << 1 |
@@ -1883,14 +1917,14 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Do measurement logging */
if (logfileid != -1 && (log_raw_measurements || synced_packet)) {
LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d %1d%1d%1d %1d%1d%1d%d %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e %08"PRIX32" %1d%1c %1c %1c",
UTI_TimeToLogForm(sample_time.tv_sec),
UTI_TimeToLogForm(sample.time.tv_sec),
UTI_IPToString(&inst->remote_addr.ip_addr),
leap_chars[pkt_leap],
message->stratum,
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
inst->local_poll, message->poll,
inst->poll_score,
offset, delay, dispersion,
sample.offset, sample.peer_delay, sample.peer_dispersion,
pkt_root_delay, pkt_root_dispersion, pkt_refid,
NTP_LVM_TO_MODE(message->lvm), interleaved_packet ? 'I' : 'B',
tss_chars[local_transmit.source],
@@ -2039,7 +2073,7 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
/* It's not a reply to our request, don't return success */
return 0;
} else {
DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%d", pkt_mode, inst->mode);
DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%u", pkt_mode, inst->mode);
return 0;
}
}
@@ -2098,7 +2132,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Fall through */
default:
/* Discard */
DEBUG_LOG("NTP packet discarded pkt_mode=%d", pkt_mode);
DEBUG_LOG("NTP packet discarded pkt_mode=%u", pkt_mode);
return;
}
@@ -2124,7 +2158,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
break;
default:
/* Discard packets in other modes */
DEBUG_LOG("NTP packet discarded auth_mode=%d", auth_mode);
DEBUG_LOG("NTP packet discarded auth_mode=%u", auth_mode);
return;
}
}
@@ -2270,61 +2304,75 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
if (!UTI_IsZeroTimespec(&inst->init_local_rx.ts))
UTI_AdjustTimespec(&inst->init_local_rx.ts, when, &inst->init_local_rx.ts, &delta, dfreq,
doffset);
if (inst->filter)
SPF_SlewSamples(inst->filter, when, dfreq, doffset);
}
/* ================================================== */
void
NCR_TakeSourceOnline(NCR_Instance inst)
NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity)
{
switch (inst->opmode) {
case MD_ONLINE:
/* Nothing to do */
char *s;
s = UTI_IPToString(&inst->remote_addr.ip_addr);
if (connectivity == SRC_MAYBE_ONLINE)
connectivity = NIO_IsServerConnectable(&inst->remote_addr) ? SRC_ONLINE : SRC_OFFLINE;
switch (connectivity) {
case SRC_ONLINE:
switch (inst->opmode) {
case MD_ONLINE:
/* Nothing to do */
break;
case MD_OFFLINE:
LOG(LOGS_INFO, "Source %s online", s);
inst->opmode = MD_ONLINE;
NCR_ResetInstance(inst);
start_initial_timeout(inst);
break;
case MD_BURST_WAS_ONLINE:
/* Will revert */
break;
case MD_BURST_WAS_OFFLINE:
inst->opmode = MD_BURST_WAS_ONLINE;
LOG(LOGS_INFO, "Source %s online", s);
break;
default:
assert(0);
}
break;
case MD_OFFLINE:
LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
inst->opmode = MD_ONLINE;
NCR_ResetInstance(inst);
start_initial_timeout(inst);
break;
case MD_BURST_WAS_ONLINE:
/* Will revert */
break;
case MD_BURST_WAS_OFFLINE:
inst->opmode = MD_BURST_WAS_ONLINE;
LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
case SRC_OFFLINE:
switch (inst->opmode) {
case MD_ONLINE:
LOG(LOGS_INFO, "Source %s offline", s);
take_offline(inst);
break;
case MD_OFFLINE:
break;
case MD_BURST_WAS_ONLINE:
inst->opmode = MD_BURST_WAS_OFFLINE;
LOG(LOGS_INFO, "Source %s offline", s);
break;
case MD_BURST_WAS_OFFLINE:
break;
default:
assert(0);
}
break;
default:
assert(0);
}
}
/* ================================================== */
void
NCR_TakeSourceOffline(NCR_Instance inst)
{
switch (inst->opmode) {
case MD_ONLINE:
LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
take_offline(inst);
break;
case MD_OFFLINE:
break;
case MD_BURST_WAS_ONLINE:
inst->opmode = MD_BURST_WAS_OFFLINE;
LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
break;
case MD_BURST_WAS_OFFLINE:
break;
}
}
/* ================================================== */
void
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
{
if (new_minpoll < MIN_MINPOLL || new_minpoll > MAX_POLL)
if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL)
return;
inst->minpoll = new_minpoll;
LOG(LOGS_INFO, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
@@ -2337,7 +2385,7 @@ NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
void
NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
{
if (new_maxpoll < MIN_MAXPOLL || new_maxpoll > MAX_POLL)
if (new_maxpoll < MIN_POLL || new_maxpoll > MAX_POLL)
return;
inst->maxpoll = new_maxpoll;
LOG(LOGS_INFO, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);
@@ -2351,7 +2399,7 @@ void
NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
{
inst->max_delay = CLAMP(0.0, new_max_delay, MAX_MAXDELAY);
LOG(LOGS_INFO, "Source %s new max delay %f",
LOG(LOGS_INFO, "Source %s new maxdelay %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay);
}
@@ -2361,7 +2409,7 @@ void
NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
{
inst->max_delay_ratio = CLAMP(0.0, new_max_delay_ratio, MAX_MAXDELAYRATIO);
LOG(LOGS_INFO, "Source %s new max delay ratio %f",
LOG(LOGS_INFO, "Source %s new maxdelayratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_ratio);
}
@@ -2371,7 +2419,7 @@ void
NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio)
{
inst->max_delay_dev_ratio = CLAMP(0.0, new_max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
LOG(LOGS_INFO, "Source %s new max delay dev ratio %f",
LOG(LOGS_INFO, "Source %s new maxdelaydevratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_dev_ratio);
}

View File

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

View File

@@ -142,6 +142,10 @@ prepare_socket(int family, int port_number, int client_only)
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
/* Enable non-blocking mode on server sockets */
if (!client_only && fcntl(sock_fd, F_SETFL, O_NONBLOCK))
DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
/* Prepare local address */
memset(&my_addr, 0, sizeof (my_addr));
my_addr_len = 0;
@@ -227,11 +231,11 @@ prepare_socket(int family, int port_number, int client_only)
if (family == AF_INET) {
#ifdef HAVE_IN_PKTINFO
/* We want the local IP info on server sockets */
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO");
/* Don't quit - we might survive anyway */
}
#elif defined(IP_RECVDSTADDR)
if (setsockopt(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "IP_RECVDSTADDR");
#endif
}
#ifdef FEAT_IPV6
@@ -570,6 +574,23 @@ NIO_IsServerSocket(int sock_fd)
/* ================================================== */
int
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
{
int sock_fd, r;
sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
if (sock_fd == INVALID_SOCK_FD)
return 0;
r = connect_socket(sock_fd, remote_addr);
close_socket(sock_fd);
return r;
}
/* ================================================== */
static void
process_message(struct msghdr *hdr, int length, int sock_fd)
{
@@ -621,6 +642,14 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
local_addr.ip_addr.family = IPADDR_INET4;
local_addr.if_index = ipi.ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR)
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
struct in_addr addr;
memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr));
local_addr.ip_addr.addr.in4 = ntohl(addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
}
#endif
#ifdef HAVE_IN6_PKTINFO
@@ -663,7 +692,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
return;
#endif
DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%u delay=%.9f",
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
@@ -792,8 +821,8 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
msg.msg_flags = 0;
cmsglen = 0;
#ifdef HAVE_IN_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) {
#ifdef HAVE_IN_PKTINFO
struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
@@ -806,8 +835,23 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
}
if (local_addr->if_index != INVALID_IF_INDEX)
ipi->ipi_ifindex = local_addr->if_index;
#elif defined(IP_SENDSRCADDR)
struct in_addr *addr;
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof (struct in_addr)));
cmsglen += CMSG_SPACE(sizeof (struct in_addr));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_addr));
addr = (struct in_addr *)CMSG_DATA(cmsg);
addr->s_addr = htonl(local_addr->ip_addr.addr.in4);
#endif
}
#ifdef HAVE_IN6_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET6) {
@@ -824,6 +868,8 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
sizeof(ipi->ipi6_addr.s6_addr));
if (local_addr->if_index != INVALID_IF_INDEX)
ipi->ipi6_ifindex = local_addr->if_index;
}
#endif

View File

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

View File

@@ -171,6 +171,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 0;
}
if (ts_info.phc_index < 0) {
DEBUG_LOG("PHC missing on %s", req.ifr_name);
close(sock_fd);
return 0;
}
ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON;
@@ -230,7 +236,8 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
iface->clock = HCL_CreateInstance(UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -315,7 +322,7 @@ check_timestamping_option(int option)
return 0;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
DEBUG_LOG("Could not enable timestamping option %x", option);
DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option);
close(sock_fd);
return 0;
}
@@ -784,7 +791,7 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%u",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
local_addr->sock_fd, local_addr->if_index, local_ts->source);

View File

@@ -860,48 +860,14 @@ slew_sources(struct timespec *raw,
/* ================================================== */
int
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
{
SourceRecord *record;
unsigned int i;
int any;
NSR_ResolveSources();
any = 0;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr) {
if (address->family == IPADDR_UNSPEC ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
any = 1;
NCR_TakeSourceOnline(record->data);
}
}
}
if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us;
for (us = unresolved_sources; us; us = us->next) {
if (us->replacement)
continue;
any = 1;
us->new_source.params.online = 1;
}
}
return any;
}
/* ================================================== */
int
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
SourceRecord *record, *syncpeer;
unsigned int i, any;
if (connectivity != SRC_OFFLINE)
NSR_ResolveSources();
any = 0;
syncpeer = NULL;
for (i = 0; i < ARR_GetSize(records); i++) {
@@ -914,15 +880,14 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
syncpeer = record;
continue;
}
NCR_TakeSourceOffline(record->data);
NCR_SetConnectivity(record->data, connectivity);
}
}
}
/* Take sync peer offline as last to avoid reference switching */
if (syncpeer) {
NCR_TakeSourceOffline(syncpeer->data);
}
/* Set the sync peer last to avoid unnecessary reference switching */
if (syncpeer)
NCR_SetConnectivity(syncpeer->data, connectivity);
if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us;
@@ -931,7 +896,7 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
if (us->replacement)
continue;
any = 1;
us->new_source.params.online = 0;
us->new_source.params.connectivity = connectivity;
}
}

View File

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

View File

@@ -119,6 +119,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */
};
static const uint16_t reply_lengths[] = {

View File

@@ -700,7 +700,7 @@ PRV_StartHelper(void)
}
/* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN);
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
helper_main(sock_pair[1]);

View File

@@ -37,6 +37,7 @@
#include "sources.h"
#include "logging.h"
#include "regress.h"
#include "samplefilt.h"
#include "sched.h"
/* list of refclock drivers */
@@ -81,13 +82,13 @@ struct RCL_Instance_Record {
int max_lock_age;
int stratum;
int tai;
struct MedianFilter filter;
uint32_t ref_id;
uint32_t lock_ref;
double offset;
double delay;
double precision;
double pulse_width;
SPF_Instance filter;
SCH_TimeoutID timeout_id;
SRC_Instance source;
};
@@ -105,18 +106,6 @@ static void slew_samples(struct timespec *raw, struct timespec *cooked, double d
static void add_dispersion(double dispersion, void *anything);
static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
static void filter_fini(struct MedianFilter *filter);
static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion);
static int filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
static int filter_get_samples(struct MedianFilter *filter);
static int filter_select_samples(struct MedianFilter *filter);
static int filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset);
static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
static RCL_Instance
get_refclock(unsigned int index)
{
@@ -151,7 +140,7 @@ RCL_Finalise(void)
if (inst->driver->fini)
inst->driver->fini(inst);
filter_fini(&inst->filter);
SPF_DestroyInstance(inst->filter);
Free(inst->driver_parameter);
SRC_DestroyInstance(inst->source);
Free(inst);
@@ -258,7 +247,11 @@ RCL_AddRefclock(RefclockParameters *params)
if (inst->driver->init && !inst->driver->init(inst))
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
/* Require the filter to have at least 4 samples to produce a filtered
sample, or be full for shorter lengths, and combine 60% of samples
closest to the median */
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
params->max_dispersion, 0.6);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples, 0.0, 0.0);
@@ -379,6 +372,28 @@ convert_tai_offset(struct timespec *sample_time, double *offset)
return 1;
}
static int
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
{
NTP_Sample sample;
sample.time = *sample_time;
sample.offset = offset;
sample.peer_delay = instance->delay;
sample.root_delay = instance->delay;
sample.peer_dispersion = dispersion;
sample.root_dispersion = dispersion;
sample.leap = instance->leap_status;
/* Handle special case when PPS is used with the local reference */
if (instance->pps_active && instance->lock_ref == -1)
sample.stratum = pps_stratum(instance, &sample.time);
else
sample.stratum = instance->stratum;
return SPF_AccumulateSample(instance->filter, &sample);
}
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
@@ -413,7 +428,10 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
return 0;
}
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
if (!accumulate_sample(instance, &cooked_time,
offset - correction + instance->offset, dispersion))
return 0;
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
@@ -489,20 +507,19 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
if (instance->lock_ref != -1) {
RCL_Instance lock_refclock;
struct timespec ref_sample_time;
double sample_diff, ref_offset, ref_dispersion, shift;
NTP_Sample ref_sample;
double sample_diff, shift;
lock_refclock = get_refclock(instance->lock_ref);
if (!filter_get_last_sample(&lock_refclock->filter,
&ref_sample_time, &ref_offset, &ref_dispersion)) {
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample_time);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff);
@@ -510,26 +527,27 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
}
/* Align the offset to the reference sample */
if ((ref_offset - offset) >= 0.0)
shift = (long)((ref_offset - offset) * rate + 0.5) / (double)rate;
if ((ref_sample.offset - offset) >= 0.0)
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_offset - offset) * rate - 0.5) / (double)rate;
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
offset += shift;
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
if (fabs(ref_sample.offset - offset) +
ref_sample.root_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_offset - offset, ref_dispersion, dispersion);
ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
return 0;
}
if (!check_pulse_edge(instance, ref_offset - offset, 0.0))
if (!check_pulse_edge(instance, ref_sample.offset - offset, 0.0))
return 0;
leap = lock_refclock->leap_status;
DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
offset, ref_offset - offset, sample_diff);
offset, ref_sample.offset - offset, sample_diff);
} else {
struct timespec ref_time;
int is_synchronised, stratum;
@@ -547,7 +565,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
offset, leap != LEAP_Unsynchronised, distance);
/* Drop also all stored samples */
filter_reset(&instance->filter);
SPF_DropSamples(instance->filter);
return 0;
}
@@ -555,7 +573,9 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 0;
}
filter_add_sample(&instance->filter, cooked_time, offset, dispersion);
if (!accumulate_sample(instance, cooked_time, offset, dispersion))
return 0;
instance->leap_status = leap;
instance->pps_active = 1;
@@ -584,17 +604,13 @@ RCL_GetDriverPoll(RCL_Instance instance)
static int
valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
{
struct timespec now, last_sample_time;
double diff, last_offset, last_dispersion;
struct timespec now;
double diff;
LCL_ReadCookedTime(&now, NULL);
diff = UTI_DiffTimespecsToDouble(&now, sample_time);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
(filter_get_samples(&instance->filter) > 0 &&
filter_get_last_sample(&instance->filter, &last_sample_time,
&last_offset, &last_dispersion) &&
UTI_CompareTimespecs(&last_sample_time, sample_time) >= 0)) {
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) {
DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
UTI_RefidToString(instance->ref_id),
UTI_TimespecToString(sample_time), diff);
@@ -638,6 +654,7 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
static void
poll_timeout(void *arg)
{
NTP_Sample sample;
int poll;
RCL_Instance inst = (RCL_Instance)arg;
@@ -651,26 +668,14 @@ poll_timeout(void *arg)
}
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion;
struct timespec sample_time;
int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
inst->driver_polled = 0;
if (sample_ok) {
if (inst->pps_active && inst->lock_ref == -1)
/* Handle special case when PPS is used with local stratum */
stratum = pps_stratum(inst, &sample_time);
else
stratum = inst->stratum;
if (SPF_GetFilteredSample(inst->filter, &sample)) {
SRC_UpdateReachability(inst->source, 1);
SRC_AccumulateSample(inst->source, &sample_time, offset,
inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);
log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion);
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);
}
@@ -687,9 +692,9 @@ slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
for (i = 0; i < ARR_GetSize(refclocks); i++) {
if (change_type == LCL_ChangeUnknownStep)
filter_reset(&get_refclock(i)->filter);
SPF_DropSamples(get_refclock(i)->filter);
else
filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset);
SPF_SlewSamples(get_refclock(i)->filter, cooked, dfreq, doffset);
}
}
@@ -699,7 +704,7 @@ add_dispersion(double dispersion, void *anything)
unsigned int i;
for (i = 0; i < ARR_GetSize(refclocks); i++)
filter_add_dispersion(&get_refclock(i)->filter, dispersion);
SPF_AddDispersion(get_refclock(i)->filter, dispersion);
}
static void
@@ -731,320 +736,3 @@ log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, in
dispersion);
}
}
static void
filter_init(struct MedianFilter *filter, int length, double max_dispersion)
{
if (length < 1)
length = 1;
filter->length = length;
filter->index = -1;
filter->used = 0;
filter->last = -1;
/* set first estimate to system precision */
filter->avg_var_n = 0;
filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
filter->max_var = max_dispersion * max_dispersion;
filter->samples = MallocArray(struct FilterSample, filter->length);
filter->selected = MallocArray(int, filter->length);
filter->x_data = MallocArray(double, filter->length);
filter->y_data = MallocArray(double, filter->length);
filter->w_data = MallocArray(double, filter->length);
}
static void
filter_fini(struct MedianFilter *filter)
{
Free(filter->samples);
Free(filter->selected);
Free(filter->x_data);
Free(filter->y_data);
Free(filter->w_data);
}
static void
filter_reset(struct MedianFilter *filter)
{
filter->index = -1;
filter->used = 0;
}
static double
filter_get_avg_sample_dispersion(struct MedianFilter *filter)
{
return sqrt(filter->avg_var);
}
static void
filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
{
filter->index++;
filter->index %= filter->length;
filter->last = filter->index;
if (filter->used < filter->length)
filter->used++;
filter->samples[filter->index].sample_time = *sample_time;
filter->samples[filter->index].offset = offset;
filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG("filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
}
static int
filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
if (filter->last < 0)
return 0;
*sample_time = filter->samples[filter->last].sample_time;
*offset = filter->samples[filter->last].offset;
*dispersion = filter->samples[filter->last].dispersion;
return 1;
}
static int
filter_get_samples(struct MedianFilter *filter)
{
return filter->used;
}
static const struct FilterSample *tmp_sorted_array;
static int
sample_compare(const void *a, const void *b)
{
const struct FilterSample *s1, *s2;
s1 = &tmp_sorted_array[*(int *)a];
s2 = &tmp_sorted_array[*(int *)b];
if (s1->offset < s2->offset)
return -1;
else if (s1->offset > s2->offset)
return 1;
return 0;
}
int
filter_select_samples(struct MedianFilter *filter)
{
int i, j, k, o, from, to, *selected;
double min_dispersion;
if (filter->used < 1)
return 0;
/* for lengths below 4 require full filter,
for 4 and above require at least 4 samples */
if ((filter->length < 4 && filter->used != filter->length) ||
(filter->length >= 4 && filter->used < 4))
return 0;
selected = filter->selected;
if (filter->used > 4) {
/* select samples with dispersion better than 1.5 * minimum */
for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) {
if (min_dispersion > filter->samples[i].dispersion)
min_dispersion = filter->samples[i].dispersion;
}
for (i = j = 0; i < filter->used; i++) {
if (filter->samples[i].dispersion <= 1.5 * min_dispersion)
selected[j++] = i;
}
} else {
j = 0;
}
if (j < 4) {
/* select all samples */
for (j = 0; j < filter->used; j++)
selected[j] = j;
}
/* and sort their indices by offset */
tmp_sorted_array = filter->samples;
qsort(selected, j, sizeof (int), sample_compare);
/* select 60 percent of the samples closest to the median */
if (j > 2) {
from = j / 5;
if (from < 1)
from = 1;
to = j - from;
} else {
from = 0;
to = j;
}
/* mark unused samples and sort the rest from oldest to newest */
o = filter->used - filter->index - 1;
for (i = 0; i < from; i++)
selected[i] = -1;
for (; i < to; i++)
selected[i] = (selected[i] + o) % filter->used;
for (; i < filter->used; i++)
selected[i] = -1;
for (i = from; i < to; i++) {
j = selected[i];
selected[i] = -1;
while (j != -1 && selected[j] != j) {
k = selected[j];
selected[j] = j;
j = k;
}
}
for (i = j = 0, k = -1; i < filter->used; i++) {
if (selected[i] != -1)
selected[j++] = (selected[i] + filter->used - o) % filter->used;
}
return j;
}
static int
filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
struct FilterSample *s, *ls;
int i, n, dof;
double x, y, d, e, var, prev_avg_var;
n = filter_select_samples(filter);
if (n < 1)
return 0;
ls = &filter->samples[filter->selected[n - 1]];
/* prepare data */
for (i = 0; i < n; i++) {
s = &filter->samples[filter->selected[i]];
filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
filter->y_data[i] = s->offset;
filter->w_data[i] = s->dispersion;
}
/* mean offset, sample time and sample dispersion */
for (i = 0, x = y = e = 0.0; i < n; i++) {
x += filter->x_data[i];
y += filter->y_data[i];
e += filter->w_data[i];
}
x /= n;
y /= n;
e /= n;
if (n >= 4) {
double b0, b1, s2, sb0, sb1;
/* set y axis to the mean sample time */
for (i = 0; i < n; i++)
filter->x_data[i] -= x;
/* make a linear fit and use the estimated standard deviation of intercept
as dispersion */
RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
&b0, &b1, &s2, &sb0, &sb1);
var = s2;
d = sb0;
dof = n - 2;
} else if (n >= 2) {
for (i = 0, d = 0.0; i < n; i++)
d += (filter->y_data[i] - y) * (filter->y_data[i] - y);
var = d / (n - 1);
d = sqrt(var);
dof = n - 1;
} else {
var = filter->avg_var;
d = sqrt(var);
dof = 1;
}
/* avoid having zero dispersion */
if (var < 1e-20) {
var = 1e-20;
d = sqrt(var);
}
/* drop the sample if variance is larger than allowed maximum */
if (filter->max_var > 0.0 && var > filter->max_var) {
DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
sqrt(var), sqrt(filter->max_var));
return 0;
}
prev_avg_var = filter->avg_var;
/* update exponential moving average of the variance */
if (filter->avg_var_n > 50) {
filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
} else {
filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
(dof + filter->avg_var_n);
if (filter->avg_var_n == 0)
prev_avg_var = filter->avg_var;
filter->avg_var_n += dof;
}
/* reduce noise in sourcestats weights by using the long-term average
instead of the estimated variance if it's not significantly lower */
if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
d = sqrt(filter->avg_var) * d / sqrt(var);
if (d < e)
d = e;
UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
*offset = y;
*dispersion = d;
filter_reset(filter);
return 1;
}
static void
filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
{
int i, first, last;
double delta_time;
struct timespec *sample;
if (filter->last < 0)
return;
/* always slew the last sample as it may be needed by PPS refclocks */
if (filter->used > 0) {
first = 0;
last = filter->used - 1;
} else {
first = last = filter->last;
}
for (i = first; i <= last; i++) {
sample = &filter->samples[i].sample_time;
UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
filter->samples[i].offset -= delta_time;
}
}
static void
filter_add_dispersion(struct MedianFilter *filter, double dispersion)
{
int i;
for (i = 0; i < filter->used; i++) {
filter->samples[i].dispersion += dispersion;
}
}

View File

@@ -80,7 +80,7 @@ static int phc_initialise(RCL_Instance instance)
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))

View File

@@ -59,24 +59,24 @@ static int pps_initialise(RCL_Instance instance) {
fd = open(path, O_RDWR);
if (fd < 0) {
LOG_FATAL("open() failed on %s", path);
LOG_FATAL("Could not open %s : %s", path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) {
LOG_FATAL("time_pps_create() failed on %s", path);
LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getcap(handle, &mode) < 0) {
LOG_FATAL("time_pps_getcap() failed on %s", path);
LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getparams(handle, &params) < 0) {
LOG_FATAL("time_pps_getparams() failed on %s", path);
LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
return 0;
}
@@ -97,7 +97,7 @@ static int pps_initialise(RCL_Instance instance) {
}
if (time_pps_setparams(handle, &params) < 0) {
LOG_FATAL("time_pps_setparams() failed on %s", path);
LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
return 0;
}
@@ -147,7 +147,7 @@ static int pps_poll(RCL_Instance instance)
if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
DEBUG_LOG("PPS sample ignored seq=%lu ts=%s",
seq, UTI_TimespecToString(&ts));
(unsigned long)seq, UTI_TimespecToString(&ts));
return 0;
}

View File

@@ -69,13 +69,13 @@ static int shm_initialise(RCL_Instance instance) {
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
if (id == -1) {
LOG_FATAL("shmget() failed");
LOG_FATAL("shmget() failed : %s", strerror(errno));
return 0;
}
shm = (struct shmTime *)shmat(id, 0, 0);
if ((long)shm == -1) {
LOG_FATAL("shmat() failed");
LOG_FATAL("shmat() failed : %s", strerror(errno));
return 0;
}

View File

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

View File

@@ -325,23 +325,6 @@ REF_GetLeapMode(void)
return leap_mode;
}
/* ================================================== */
static double
Sqr(double x)
{
return x*x;
}
/* ================================================== */
#if 0
static double
Cube(double x)
{
return x*x*x;
}
#endif
/* ================================================== */
/* Update the drift coefficients to the file. */
@@ -528,7 +511,7 @@ maybe_log_offset(double offset, time_t now)
double abs_offset;
FILE *p;
char buffer[BUFLEN], host[BUFLEN];
struct tm stm;
struct tm *tm;
abs_offset = fabs(offset);
@@ -539,17 +522,25 @@ maybe_log_offset(double offset, time_t now)
if (do_mail_change &&
(abs_offset > mail_change_threshold)) {
snprintf(buffer, sizeof(buffer), "%s %." S_MAX_USER_LEN "s", MAIL_PROGRAM, mail_change_user);
snprintf(buffer, sizeof (buffer), "%s -t", MAIL_PROGRAM);
p = popen(buffer, "w");
if (p) {
if (gethostname(host, sizeof(host)) < 0) {
strcpy(host, "<UNKNOWN>");
}
host[sizeof (host) - 1] = '\0';
fprintf(p, "To: %s\n", mail_change_user);
fprintf(p, "Subject: chronyd reports change to system clock on node [%s]\n", host);
fputs("\n", p);
stm = *localtime(&now);
strftime(buffer, sizeof(buffer), "On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", &stm);
fputs(buffer, p);
tm = localtime(&now);
if (tm) {
strftime(buffer, sizeof (buffer),
"On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", tm);
fputs(buffer, p);
}
/* If offset < 0 the local clock is slow, so we are applying a
positive change to it to bring it into line, hence the
negation of 'offset' in the next statement (and earlier) */
@@ -929,34 +920,58 @@ special_mode_sync(int valid, double offset)
/* ================================================== */
void
REF_SetReference(int stratum,
NTP_Leap leap,
int combined_sources,
uint32_t ref_id,
IPAddr *ref_ip,
struct timespec *ref_time,
double offset,
double offset_sd,
double frequency,
double skew,
double root_delay,
double root_dispersion
)
static void
get_clock_estimates(int manual,
double measured_freq, double measured_skew,
double *estimated_freq, double *estimated_skew,
double *residual_freq)
{
double gain, expected_freq, expected_skew, extra_skew;
/* We assume that the local clock is running according to our previously
determined value */
expected_freq = 0.0;
expected_skew = our_skew;
/* Set new frequency based on weighted average of the expected and measured
skew. Disable updates that are based on totally unreliable frequency
information unless it is a manual reference. */
if (manual) {
gain = 1.0;
} else if (fabs(measured_skew) > max_update_skew) {
DEBUG_LOG("Skew %f too large to track", measured_skew);
gain = 0.0;
} else {
gain = 3.0 * SQUARE(expected_skew) /
(3.0 * SQUARE(expected_skew) + SQUARE(measured_skew));
}
gain = CLAMP(0.0, gain, 1.0);
*estimated_freq = expected_freq + gain * (measured_freq - expected_freq);
*residual_freq = measured_freq - *estimated_freq;
extra_skew = sqrt(SQUARE(expected_freq - *estimated_freq) * (1.0 - gain) +
SQUARE(measured_freq - *estimated_freq) * gain);
*estimated_skew = expected_skew + gain * (measured_skew - expected_skew) + extra_skew;
}
/* ================================================== */
void
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
double offset, double offset_sd,
double frequency, double frequency_sd, double skew,
double root_delay, double root_dispersion)
{
double previous_skew, new_skew;
double previous_freq, new_freq;
double old_weight, new_weight, sum_weight;
double delta_freq1, delta_freq2;
double skew1, skew2;
double our_offset;
double our_frequency;
double abs_freq_ppm;
double update_interval;
double elapsed, correction_rate, orig_root_distance;
double uncorrected_offset, accumulate_offset, step_offset;
double residual_frequency, local_abs_frequency;
double elapsed, update_interval, correction_rate, orig_root_distance;
struct timespec now, raw_now;
NTP_int64 ref_fuzz;
int manual;
assert(initialised);
@@ -966,23 +981,33 @@ REF_SetReference(int stratum,
return;
}
/* Guard against dividing by zero and NaN */
if (!(skew > MIN_SKEW))
skew = MIN_SKEW;
manual = leap == LEAP_Unsynchronised;
LCL_ReadRawTime(&raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
our_offset = offset + elapsed * frequency;
offset += elapsed * frequency;
offset_sd += elapsed * frequency_sd;
if (!is_offset_ok(our_offset))
if (last_ref_update.tv_sec) {
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
update_interval = MAX(update_interval, 0.0);
} else {
update_interval = 0.0;
}
/* Get new estimates of the frequency and skew including the new data */
get_clock_estimates(manual, frequency, skew,
&frequency, &skew, &residual_frequency);
if (!is_offset_ok(offset))
return;
orig_root_distance = our_root_delay / 2.0 + get_root_dispersion(&now);
are_we_synchronised = leap != LEAP_Unsynchronised ? 1 : 0;
are_we_synchronised = leap != LEAP_Unsynchronised;
our_stratum = stratum + 1;
our_ref_id = ref_id;
if (ref_ip)
@@ -990,17 +1015,13 @@ REF_SetReference(int stratum,
else
our_ref_ip.family = IPADDR_UNSPEC;
our_ref_time = *ref_time;
our_skew = skew;
our_residual_freq = residual_frequency;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
if (last_ref_update.tv_sec) {
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
if (update_interval < 0.0)
update_interval = 0.0;
} else {
update_interval = 0.0;
}
last_ref_update = now;
last_ref_update_interval = update_interval;
last_offset = offset;
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
@@ -1018,68 +1039,28 @@ REF_SetReference(int stratum,
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */
if (is_step_limit_reached(our_offset, uncorrected_offset)) {
if (is_step_limit_reached(offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */
accumulate_offset = uncorrected_offset;
step_offset = our_offset - uncorrected_offset;
step_offset = offset - uncorrected_offset;
} else {
accumulate_offset = our_offset;
accumulate_offset = offset;
step_offset = 0.0;
}
/* Eliminate updates that are based on totally unreliable frequency
information. Ignore this limit with manual reference. */
if (fabs(skew) < max_update_skew || leap == LEAP_Unsynchronised) {
previous_skew = our_skew;
new_skew = skew;
previous_freq = 0.0; /* We assume that the local clock is running
according to our previously determined
value; note that this is a delta frequency
--- absolute frequencies are only known in
the local module. */
new_freq = frequency;
/* Set new frequency based on weighted average of old and new skew. With
manual reference the old frequency has no weight. */
old_weight = leap != LEAP_Unsynchronised ? 1.0 / Sqr(previous_skew) : 0.0;
new_weight = 3.0 / Sqr(new_skew);
sum_weight = old_weight + new_weight;
our_frequency = (previous_freq * old_weight + new_freq * new_weight) / sum_weight;
delta_freq1 = previous_freq - our_frequency;
delta_freq2 = new_freq - our_frequency;
skew1 = sqrt((Sqr(delta_freq1) * old_weight + Sqr(delta_freq2) * new_weight) / sum_weight);
skew2 = (previous_skew * old_weight + new_skew * new_weight) / sum_weight;
our_skew = skew1 + skew2;
our_residual_freq = new_freq - our_frequency;
LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
/* Adjust the clock */
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
} else {
DEBUG_LOG("Skew %f too large to track, offset=%f", skew, accumulate_offset);
LCL_AccumulateOffset(accumulate_offset, correction_rate);
our_residual_freq = frequency;
}
update_leap_status(leap, raw_now.tv_sec, 0);
maybe_log_offset(our_offset, raw_now.tv_sec);
maybe_log_offset(offset, raw_now.tv_sec);
if (step_offset != 0.0) {
if (LCL_ApplyStepOffset(step_offset))
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
}
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
LCL_SetSyncStatus(are_we_synchronised, offset_sd,
root_delay / 2.0 + get_root_dispersion(&now));
/* Add a random error of up to one second to the reference time to make it
less useful when disclosed to NTP and cmdmon clients for estimating
@@ -1090,36 +1071,33 @@ REF_SetReference(int stratum,
if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
our_ref_time.tv_sec--;
abs_freq_ppm = LCL_ReadAbsoluteFrequency();
local_abs_frequency = LCL_ReadAbsoluteFrequency();
write_log(&now, combined_sources, abs_freq_ppm, our_offset, offset_sd,
uncorrected_offset, orig_root_distance);
write_log(&now, combined_sources, local_abs_frequency,
offset, offset_sd, uncorrected_offset, orig_root_distance);
if (drift_file) {
/* Update drift file at most once per hour */
drift_file_age += update_interval;
if (drift_file_age < 0.0 || drift_file_age > 3600.0) {
update_drift_file(abs_freq_ppm, our_skew);
update_drift_file(local_abs_frequency, our_skew);
drift_file_age = 0.0;
}
}
/* Update fallback drifts */
if (fb_drifts && are_we_synchronised) {
update_fb_drifts(abs_freq_ppm, update_interval);
update_fb_drifts(local_abs_frequency, update_interval);
schedule_fb_drift(&now);
}
last_ref_update_interval = update_interval;
last_offset = our_offset;
/* Update the moving average of squares of offset, quickly on start */
if (avg2_moving) {
avg2_offset += 0.1 * (our_offset * our_offset - avg2_offset);
avg2_offset += 0.1 * (SQUARE(offset) - avg2_offset);
} else {
if (avg2_offset > 0.0 && avg2_offset < our_offset * our_offset)
if (avg2_offset > 0.0 && avg2_offset < SQUARE(offset))
avg2_moving = 1;
avg2_offset = our_offset * our_offset;
avg2_offset = SQUARE(offset);
}
}
@@ -1138,7 +1116,7 @@ REF_SetManualReference
only supposed to be used with the local source option, really.
Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */
REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL,
ref_time, offset, 0.0, frequency, skew, 0.0, 0.0);
ref_time, offset, 0.0, frequency, skew, skew, 0.0, 0.0);
}
/* ================================================== */

View File

@@ -144,6 +144,7 @@ extern void REF_SetReference
double offset,
double offset_sd,
double frequency,
double frequency_sd,
double skew,
double root_delay,
double root_dispersion

View File

@@ -352,7 +352,7 @@ rtc_from_t(const time_t *t)
static time_t
t_from_rtc(struct tm *stm) {
struct tm temp1, temp2;
struct tm temp1, temp2, *tm;
long diff;
time_t t1, t2;
@@ -360,12 +360,14 @@ t_from_rtc(struct tm *stm) {
temp1.tm_isdst = 0;
t1 = mktime(&temp1);
if (rtc_on_utc) {
temp2 = *gmtime(&t1);
} else {
temp2 = *localtime(&t1);
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
if (!tm) {
DEBUG_LOG("gmtime()/localtime() failed");
return -1;
}
temp2 = *tm;
temp2.tm_isdst = 0;
t2 = mktime(&temp2);
diff = t2 - t1;

453
samplefilt.c Normal file
View File

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

49
samplefilt.h Normal file
View File

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

View File

@@ -534,7 +534,8 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
if (except_fds && FD_ISSET(fd, except_fds)) {
/* This descriptor has an exception, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
if (ptr->handler)
(ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
nfd--;
/* Don't try to read from it now */
@@ -547,14 +548,16 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
if (read_fds && FD_ISSET(fd, read_fds)) {
/* This descriptor can be read from, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
if (ptr->handler)
(ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
nfd--;
}
if (write_fds && FD_ISSET(fd, write_fds)) {
/* This descriptor can be written to, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
if (ptr->handler)
(ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
nfd--;
}
}

View File

@@ -144,7 +144,7 @@ update_stages(void)
is equal to the offset that should be smoothed out */
s1 = smooth_offset / max_wander;
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
s2 = SQUARE(smooth_freq) / (2.0 * SQUARE(max_wander));
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. The direction of the 1st stage is selected so that

View File

@@ -54,6 +54,7 @@ static int initialised = 0;
/* ================================================== */
/* Structure used to hold info for selecting between sources */
struct SelectInfo {
NTP_Leap leap;
int stratum;
int select_ok;
double std_dev;
@@ -91,7 +92,6 @@ typedef enum {
source */
struct SRC_Instance_Record {
SST_Stats stats;
NTP_Leap leap_status; /* Leap status */
int index; /* Index back into the array of source */
uint32_t ref_id; /* The reference ID of this source
(i.e. from its IP address, NOT the
@@ -291,7 +291,6 @@ void SRC_DestroyInstance(SRC_Instance instance)
void
SRC_ResetInstance(SRC_Instance instance)
{
instance->leap_status = LEAP_Normal;
instance->active = 0;
instance->updates = 0;
instance->reachability = 0;
@@ -330,39 +329,24 @@ SRC_GetSourcestats(SRC_Instance instance)
This function causes the frequency estimation to be re-run for the
designated source, and the clock selection procedure to be re-run
afterwards.
Parameters are described in sources.h
*/
void SRC_AccumulateSample
(SRC_Instance inst,
struct timespec *sample_time,
double offset,
double peer_delay,
double peer_dispersion,
double root_delay,
double root_dispersion,
int stratum,
NTP_Leap leap_status)
void
SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
{
assert(initialised);
inst->leap_status = leap_status;
DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
root_delay, root_dispersion, stratum);
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
sample->root_delay, sample->root_dispersion, sample->stratum);
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, "Dropping sample around leap second");
return;
}
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
IS FLIPPED */
SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
SST_AccumulateSample(inst->stats, sample);
SST_DoNewRegression(inst->stats);
}
@@ -512,20 +496,21 @@ mark_ok_sources(SRC_Status status)
static int
combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
double *offset_sd, double *frequency, double *skew)
double *offset_sd, double *frequency, double *frequency_sd, double *skew)
{
struct timespec src_ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew;
double frequency_weight, sum_frequency_weight, sum_frequency;
double inv_sum2_frequency_sd, inv_sum2_skew;
int i, index, combined;
if (n_sel_sources == 1)
return 1;
sum_offset_weight = sum_offset = sum2_offset_sd = 0.0;
sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0;
sum_frequency_weight = sum_frequency = inv_sum2_frequency_sd = inv_sum2_skew = 0.0;
sel_src_distance = sources[selected_source_index]->sel_info.root_distance;
if (sources[selected_source_index]->type == SRC_NTP)
@@ -535,7 +520,7 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
index = sel_sources[i];
SST_GetTrackingData(sources[index]->stats, &src_ref_time,
&src_offset, &src_offset_sd,
&src_frequency, &src_skew,
&src_frequency, &src_frequency_sd, &src_skew,
&src_root_delay, &src_root_dispersion);
/* Don't include this source if its distance is longer than the distance of
@@ -563,20 +548,23 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency;
src_offset_sd += elapsed * src_frequency_sd;
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / src_skew;
frequency_weight = 1.0 / SQUARE(src_frequency_sd);
DEBUG_LOG("combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd,
frequency_weight, src_frequency, src_frequency_sd, src_skew);
sum_offset_weight += offset_weight;
sum_offset += offset_weight * src_offset;
sum2_offset_sd += offset_weight * (src_offset_sd * src_offset_sd +
(src_offset - *offset) * (src_offset - *offset));
sum2_offset_sd += offset_weight * (SQUARE(src_offset_sd) +
SQUARE(src_offset - *offset));
sum_frequency_weight += frequency_weight;
sum_frequency += frequency_weight * src_frequency;
inv_sum2_skew += 1.0 / (src_skew * src_skew);
inv_sum2_frequency_sd += 1.0 / SQUARE(src_frequency_sd);
inv_sum2_skew += 1.0 / SQUARE(src_skew);
combined++;
}
@@ -585,10 +573,11 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
*offset = sum_offset / sum_offset_weight;
*offset_sd = sqrt(sum2_offset_sd / sum_offset_weight);
*frequency = sum_frequency / sum_frequency_weight;
*frequency_sd = 1.0 / sqrt(inv_sum2_frequency_sd);
*skew = 1.0 / sqrt(inv_sum2_skew);
DEBUG_LOG("combined result offset=%e sd=%e freq=%e skew=%e",
*offset, *offset_sd, *frequency, *skew);
DEBUG_LOG("combined result offset=%e osd=%e freq=%e fsd=%e skew=%e",
*offset, *offset_sd, *frequency, *frequency_sd, *skew);
return combined;
}
@@ -602,12 +591,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
{
struct SelectInfo *si;
struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources;
int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
int depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score;
double first_sample_ago, max_reach_sample_ago;
@@ -635,6 +624,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_badstats_sources = 0;
sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0;
max_sel_reach_size = 0;
max_reach_sample_ago = 0.0;
for (i = 0; i < n_sources; i++) {
@@ -652,7 +642,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, &si->leap,
&si->lo_limit, &si->hi_limit, &si->root_distance,
&si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok);
@@ -694,6 +684,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (max_sel_reach < sources[i]->reachability)
max_sel_reach = sources[i]->reachability;
if (max_sel_reach_size < sources[i]->reachability_size)
max_sel_reach_size = sources[i]->reachability_size;
}
orphan_stratum = REF_GetOrphanStratum();
@@ -777,18 +770,17 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints += 2;
}
DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f",
n_badstats_sources, n_sel_sources, max_badstat_reach,
max_sel_reach, max_reach_sample_ago);
DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f",
n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach,
(unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago);
/* Wait for the next call if we have no source selected and there is
a source with bad stats (has less than 3 samples) with reachability
equal to shifted maximum reachability of sources with valid stats.
This delays selecting source on start with servers using the same
polling interval until they all have valid stats. */
if (n_badstats_sources && n_sel_sources &&
selected_source_index == INVALID_SOURCE &&
max_sel_reach >> 1 == max_badstat_reach) {
if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE &&
max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
mark_ok_sources(SRC_WAITS_STATS);
return;
}
@@ -929,9 +921,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
continue;
leap_votes++;
if (sources[index]->leap_status == LEAP_InsertSecond)
if (sources[index]->sel_info.leap == LEAP_InsertSecond)
leap_ins++;
else if (sources[index]->leap_status == LEAP_DeleteSecond)
else if (sources[index]->sel_info.leap == LEAP_DeleteSecond)
leap_del++;
}
@@ -1015,7 +1007,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
sources[i]->sel_score = 1.0 / distance;
}
DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f",
DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f",
sources[i]->sel_score, sources[i]->ref_id,
updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance);
@@ -1078,18 +1070,18 @@ SRC_SelectSource(SRC_Instance updated_inst)
SST_GetTrackingData(sources[selected_source_index]->stats, &ref_time,
&src_offset, &src_offset_sd,
&src_frequency, &src_skew,
&src_frequency, &src_frequency_sd, &src_skew,
&src_root_delay, &src_root_dispersion);
combined = combine_sources(n_sel_sources, &ref_time, &src_offset,
&src_offset_sd, &src_frequency, &src_skew);
combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd,
&src_frequency, &src_frequency_sd, &src_skew);
REF_SetReference(sources[selected_source_index]->sel_info.stratum,
leap_status, combined,
sources[selected_source_index]->ref_id,
sources[selected_source_index]->ip_addr,
&ref_time, src_offset, src_offset_sd,
src_frequency, src_skew,
src_frequency, src_frequency_sd, src_skew,
src_root_delay, src_root_dispersion);
}

View File

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

View File

@@ -132,6 +132,7 @@ struct SST_Stats_Record {
source per unit local time. (Positive => local clock fast,
negative => local clock slow) */
double estimated_frequency;
double estimated_frequency_sd;
/* This is the assumed worst case bounds on the estimated frequency.
We assume that the true frequency lies within +/- half this much
@@ -174,10 +175,11 @@ struct SST_Stats_Record {
time of the measurements */
double root_dispersions[MAX_SAMPLES];
/* This array contains the strata that were associated with the sources
at the times the samples were generated */
int strata[MAX_SAMPLES];
/* The stratum from the last accumulated sample */
int stratum;
/* The leap status from the last accumulated sample */
NTP_Leap leap;
};
/* ================================================== */
@@ -244,7 +246,8 @@ SST_ResetInstance(SST_Stats inst)
inst->best_single_sample = 0;
inst->min_delay_sample = 0;
inst->estimated_frequency = 0;
inst->skew = 2000.0e-6;
inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
inst->skew = WORST_CASE_FREQ_BOUND;
inst->estimated_offset = 0.0;
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
UTI_ZeroTimespec(&inst->offset_time);
@@ -252,6 +255,7 @@ SST_ResetInstance(SST_Stats inst)
inst->nruns = 0;
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
inst->leap = LEAP_Unsynchronised;
}
/* ================================================== */
@@ -287,11 +291,7 @@ prune_register(SST_Stats inst, int new_oldest)
/* ================================================== */
void
SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
double offset,
double peer_delay, double peer_dispersion,
double root_delay, double root_dispersion,
int stratum)
SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
{
int n, m;
@@ -303,7 +303,7 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
/* Make sure it's newer than the last sample */
if (inst->n_samples &&
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], &sample->time) >= 0) {
LOG(LOGS_WARN, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
SST_ResetInstance(inst);
@@ -313,14 +313,17 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
(MAX_SAMPLES * REGRESS_RUNS_RATIO);
m = n % MAX_SAMPLES;
inst->sample_times[n] = *sample_time;
inst->offsets[n] = offset;
inst->orig_offsets[m] = offset;
inst->peer_delays[n] = peer_delay;
inst->peer_dispersions[m] = peer_dispersion;
inst->root_delays[m] = root_delay;
inst->root_dispersions[m] = root_dispersion;
inst->strata[m] = stratum;
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
IS FLIPPED */
inst->sample_times[n] = sample->time;
inst->offsets[n] = -sample->offset;
inst->orig_offsets[m] = -sample->offset;
inst->peer_delays[n] = sample->peer_delay;
inst->peer_dispersions[m] = sample->peer_dispersion;
inst->root_delays[m] = sample->root_delay;
inst->root_dispersions[m] = sample->root_dispersion;
inst->stratum = sample->stratum;
inst->leap = sample->leap;
if (inst->peer_delays[n] < inst->fixed_min_delay)
inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n];
@@ -550,7 +553,7 @@ SST_DoNewRegression(SST_Stats inst)
sd_weight = 1.0;
if (peer_distances[i] > min_distance)
sd_weight += (peer_distances[i] - min_distance) / sd;
weights[i] = sd_weight * sd_weight;
weights[i] = SQUARE(sd_weight);
}
}
@@ -570,6 +573,7 @@ SST_DoNewRegression(SST_Stats inst)
old_freq = inst->estimated_frequency;
inst->estimated_frequency = est_slope;
inst->estimated_frequency_sd = CLAMP(MIN_SKEW, est_slope_sd, MAX_SKEW);
inst->skew = est_slope_sd * RGR_GetTCoef(degrees_of_freedom);
inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample];
@@ -600,6 +604,7 @@ SST_DoNewRegression(SST_Stats inst)
prune_register(inst, best_start);
} else {
inst->estimated_frequency = 0.0;
inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
inst->skew = WORST_CASE_FREQ_BOUND;
times_back_start = 0;
}
@@ -636,7 +641,7 @@ SST_GetFrequencyRange(SST_Stats inst,
void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
@@ -656,7 +661,8 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
i = get_runsbuf_index(inst, inst->best_single_sample);
j = get_buf_index(inst, inst->best_single_sample);
*stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
*stratum = inst->stratum;
*leap = inst->leap;
*std_dev = inst->std_dev;
sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
@@ -698,7 +704,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
void
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *frequency, double *frequency_sd, double *skew,
double *root_delay, double *root_dispersion)
{
int i, j;
@@ -713,16 +719,16 @@ SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
*average_offset = inst->estimated_offset;
*offset_sd = inst->estimated_offset_sd;
*frequency = inst->estimated_frequency;
*frequency_sd = inst->estimated_frequency_sd;
*skew = inst->skew;
*root_delay = inst->root_delays[j];
elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
DEBUG_LOG("n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew,
*average_offset, *offset_sd, *root_dispersion);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample + *offset_sd;
DEBUG_LOG("n=%d off=%f offsd=%f freq=%e freqsd=%e skew=%e delay=%f disp=%f",
inst->n_samples, *average_offset, *offset_sd,
*frequency, *frequency_sd, *skew, *root_delay, *root_dispersion);
}
/* ================================================== */
@@ -864,7 +870,7 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
inst->root_delays[j],
inst->root_dispersions[j],
1.0, /* used to be inst->weights[i] */
inst->strata[j]);
inst->stratum /* used to be an array */);
}
@@ -909,7 +915,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
&(inst->root_delays[i]),
&(inst->root_dispersions[i]),
&weight, /* not used anymore */
&(inst->strata[i])) != 10)) {
&inst->stratum) != 10)) {
/* This is the branch taken if the read FAILED */
@@ -957,7 +963,7 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->orig_latest_meas = inst->orig_offsets[j];
report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j];
report->stratum = inst->stratum;
/* Align the sample time to reduce the leak of the receive timestamp */
last_sample_time = inst->sample_times[i];

View File

@@ -51,19 +51,8 @@ extern void SST_ResetInstance(SST_Stats inst);
/* This function changes the reference ID and IP address */
extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
/* This function accumulates a single sample into the statistics handler
sample_time is the epoch at which the sample is to be considered to
have been made.
offset is the offset of the local clock relative to the source in
seconds. Positive indicates that the local clock if FAST (contrary
to the NTP parts of the software)
stratum is the stratum of the source from which the sample came.
*/
extern void SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
/* This function accumulates a single sample into the statistics handler */
extern void SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample);
/* This function runs the linear regression operation on the data. It
finds the set of most recent samples that give the tightest
@@ -80,7 +69,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */
extern void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
@@ -93,7 +82,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
extern void
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *frequency, double *frequency_sd, double *skew,
double *root_delay, double *root_dispersion);
/* This routine is called when the local machine clock parameters are

View File

@@ -29,10 +29,16 @@
#include "sources.h"
typedef enum {
SRC_OFFLINE,
SRC_ONLINE,
SRC_MAYBE_ONLINE,
} SRC_Connectivity;
typedef struct {
int minpoll;
int maxpoll;
int online;
SRC_Connectivity connectivity;
int auto_offline;
int presend_minpoll;
int burst;
@@ -43,6 +49,7 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
int filter_length;
int interleaved;
int sel_options;
uint32_t authkey;

View File

@@ -254,13 +254,7 @@ NSR_GetLocalRefid(IPAddr *address)
}
int
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
{
return 0;
}
int
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
return 0;
}

View File

@@ -42,11 +42,6 @@
#include <sys/resource.h>
#endif
#ifdef FEAT_PRIVDROP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
@@ -66,6 +61,11 @@
#endif
#endif
#ifdef FEAT_PRIVDROP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
#include "sys_linux.h"
#include "sys_timex.h"
#include "conf.h"
@@ -309,9 +309,9 @@ get_version_specific_details(void)
nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
max_tick_bias = nominal_tick / 10;
/* We can't reliably detect the internal kernel HZ, it may not even be fixed
(CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
tick_update_hz = 100;
/* In modern kernels the frequency of the clock is updated immediately in the
adjtimex() system call. Assume a maximum delay of 10 microseconds. */
tick_update_hz = 100000;
get_kernel_version(&major, &minor, &patch);
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
@@ -322,9 +322,15 @@ get_version_specific_details(void)
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
/* Tickless kernels before 2.6.33 accumulated ticks only in
half-second intervals */
/* In tickless kernels before 2.6.33 the frequency is updated in
a half-second interval */
tick_update_hz = 2;
} else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
/* In kernels before 4.19 the frequency is updated only on internal ticks
(CONFIG_HZ). As their rate cannot be reliably detected from the user
space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
assume the lowest commonly used constant rate */
tick_update_hz = 100;
}
/* ADJ_SETOFFSET support */
@@ -334,8 +340,8 @@ get_version_specific_details(void)
have_setoffset = 1;
}
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d",
hz, nominal_tick, max_tick_bias);
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
hz, nominal_tick, max_tick_bias, tick_update_hz);
}
/* ================================================== */
@@ -385,7 +391,7 @@ test_step_offset(void)
static void
report_time_adjust_blockers(void)
{
#ifdef FEAT_PRIVDROP
#if defined(FEAT_PRIVDROP) && defined(CAP_IS_SUPPORTED)
if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
return;
LOG(LOGS_WARN, "CAP_SYS_TIME not present");
@@ -529,7 +535,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
#endif
};
const static int fcntls[] = { F_GETFD, F_SETFD };
const static int fcntls[] = { F_GETFD, F_SETFD, F_SETFL };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,

View File

@@ -36,6 +36,7 @@
#include <float.h>
#include <glob.h>
#include <grp.h>
#include <inttypes.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
@@ -63,14 +64,6 @@
#include <sys/timex.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#elif HAVE_STDINT_H
#include <stdint.h>
#else
/* Tough */
#endif
#ifdef FEAT_IPV6
/* For inet_ntop() */
#include <arpa/inet.h>

View File

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

87
test/compilation/003-sanitizers Executable file
View File

@@ -0,0 +1,87 @@
#!/bin/bash
# Run the unit and simulation tests with different compiler sanitizers
# and under valgrind
cd ../..
if [ "$(uname -sm)" != "Linux x86_64" ]; then
echo Test supported on Linux x86_64 only
exit 1
fi
[ -f /etc/os-release ] && . /etc/os-release
if [ "$ID" = "fedora" ]; then
echo Checking test dependencies:
rpm -q {valgrind,gcc,clang}.x86_64 {libgcc,clang-libs}.{x86_64,i686} || exit 1
rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt}-devel.{x86_64,i686} || exit 1
echo
fi
touch Makefile
for CC in gcc clang; do
export CC
for arch_opts in "-m32" ""; do
pushd test/simulation/clknetsim || exit 1
make clean > /dev/null 2>&1
CFLAGS="$arch_opts -DCLKNETSIM_DISABLE_SYSCALL" make "$@" || exit 1
echo
popd
for extra_config_opts in \
"--all-privops" \
"--disable-scfilter" \
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt"; \
do
for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
# clang msan doesn't work on i686 and otherwise requires patches
echo $CFLAGS | grep -q 'sanitize=memory' && continue
# build fails with clang ubsan on i686 (Fedora only?)
[ "$arch_opts" = "-m32" -a "$CC" = "clang" ] && continue
[ "$CC" = "gcc" ] && echo $CFLAGS | grep -q 'sanitize=address' && CFLAGS="$CFLAGS -static-libasan"
config_opts="--with-user=chrony --enable-debug --enable-scfilter --enable-ntp-signd $extra_config_opts"
echo -----------------------------------------------------------------------------
echo CC=\"$CC\" CFLAGS=\"$CFLAGS\" ./configure $config_opts
make distclean > /dev/null 2>&1
./configure $config_opts || exit 1
if echo "$config_opts" | grep -q all-privops; then
for op in ADJUSTTIME ADJUSTTIMEX SETTIME BINDSOCKET; do
echo "#define PRIVOPS_$op 1" >> config.h
done
fi
make "$@" || exit 1
[ -n "$BUILD_TEST_ONLY" ] && continue
echo
pushd test/unit || exit 1
if [ "$san_options" = "" ]; then
make check TEST_WRAPPER="valgrind --error-exitcode=1" || exit 1
else
make check || exit 1
fi
popd
echo
pushd test/simulation || exit 1
CLKNETSIM_RANDOM_SEED=101 ./run -i 1 || exit 1
popd
done
done
done
done

View File

@@ -12,11 +12,13 @@ time_rms_limit=5e-6
freq_rms_limit=5e-6
client_conf="makestep 1e-2 1"
for poll in $(seq 2 14); do
for poll in $(seq 1 14); do
client_server_options="minpoll $poll maxpoll $poll"
limit=$[2**$poll * 10]
min_sync_time=$[2**$poll * 2]
max_sync_time=$[2**$poll * 21 / 10 + 1]
client_max_min_out_interval=$(awk "BEGIN {print 2^$poll * 1.1}")
client_min_mean_out_interval=$(awk "BEGIN {print 2^$poll * 0.99}")
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -9,19 +9,22 @@ refclock_jitter=$jitter
min_sync_time=45
max_sync_time=70
chronyc_start=70
client_conf="refclock SHM 0 stratum 3 delay 1e-3 refid GPS"
chronyc_conf="tracking"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^Reference ID.*47505300 \(GPS\)
for refclock in "SHM 0" "PHC /dev/ptp0"; do
client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^Reference ID.*47505300 \(GPS\)
Stratum.*: 4
.*
Root delay : 0.001000000 seconds
.*
Update interval : 16\.. seconds
.*$" || test_fail
done
test_pass

View File

@@ -30,4 +30,25 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
client_peer_options=""
while read lminpoll lmaxpoll rminpoll rmaxpoll max_sync_time; do
client_lpeer_options="minpoll $lminpoll maxpoll $lmaxpoll"
client_rpeer_options="minpoll $rminpoll maxpoll $rmaxpoll"
limit=$[$max_sync_time * 10]
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
done <<-EOF
3 6 3 6 400
3 3 6 6 450
6 6 3 3 450
3 6 6 6 450
6 6 3 6 450
-2 -2 2 2 220
2 2 -2 -2 220
EOF
test_pass

View File

@@ -63,4 +63,78 @@ try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
513 RTC driver not running$" \
|| test_fail
server_strata=0
chronyc_start=0
client_conf=""
limit=1
for chronyc_conf in \
"accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4" \
"allow 1.2.3.4" \
"allow 1.2" \
"allow 3.4.5" \
"allow 6.7.8/22" \
"allow 6.7.8.9/22" \
"allow 2001:db8::/32" \
"allow 0/0" \
"allow ::/0" \
"allow" \
"allow all 10/24" \
"burst 5/10" \
"burst 3/5 255.255.255.0/1.2.3.0" \
"burst 1/2 1.2.3.0/24" \
"clients" \
"cmdaccheck 1.2.3.4" \
"cmdallow 1.2.3.4" \
"cmdallow all 1.2.3.0/24" \
"cmddeny 1.2.3.4" \
"cmddeny all 1.2.3.0/24" \
"cyclelogs" \
"delete 10.0.0.0" \
"deny 1.2.3.4" \
"deny all 1.2.3.0/24" \
"dump" \
"local stratum 5 distance 1.0 orphan" \
"local off" \
"makestep 10.0 3" \
"makestep" \
"manual delete 0" \
"manual off" \
"manual on" \
"manual reset" \
"maxdelay 1.2.3.4 1e-2" \
"maxdelaydevratio 1.2.3.4 5.0" \
"maxdelayratio 1.2.3.4 3.0" \
"maxpoll 1.2.3.4 5" \
"maxupdateskew 1.2.3.4 10.0" \
"minpoll 1.2.3.4 3" \
"minstratum 1.2.3.4 1" \
"ntpdata 1.2.3.4" \
"offline" \
"offline 255.255.255.0/1.2.3.0" \
"offline 1.2.3.0/24" \
"online" \
"online 1.2.3.0/24" \
"onoffline" \
"polltarget 1.2.3.4 10" \
"refresh" \
"rekey" \
"reselect" \
"reselectdist 1e-3" \
"settime 16:30" \
"settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \
"shutdown" \
"smoothtime reset" \
"smoothtime activate" \
"trimrtc" \
"writertc"
do
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "501 Not authorised" || test_fail
done
test_pass

View File

@@ -10,6 +10,7 @@ server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
max_sync_time=500
client_start=140
chronyc_start=300
chronyc_conf="tracking"
time_rms_limit=5e-4

19
test/simulation/127-filter Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
. ./test.common
test_start "filter option"
client_server_options="minpoll 4 maxpoll 4 filter 15"
min_sync_time=710
max_sync_time=720
client_max_min_out_interval=16.1
client_min_mean_out_interval=15.9
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

25
test/simulation/128-nocontrol Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
. ./test.common
test_start "-x option"
wander=0.0
time_offset=0.0
freq_offset=0.0
time_max_limit=1e-6
freq_max_limit=1e-9
min_sync_time=0
max_sync_time=0
client_chronyd_options="-x"
chronyc_start=300
chronyc_conf="tracking"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 2.*$" || test_fail
test_pass

25
test/simulation/129-reload Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
. ./test.common
test_start "-r option"
wander=0.0
limit=100
min_sync_time=100
max_sync_time=104
client_chronyd_options="-r"
client_conf="dumpdir tmp"
run_test || test_fail
client_start=$limit
limit=1000
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

23
test/simulation/130-quit Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
. ./test.common
test_start "-q/-Q option"
wander=0.0
freq_offset=0.0
min_sync_time=5
max_sync_time=10
client_chronyd_options="-q"
client_server_options="iburst"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_chronyd_options="-Q"
run_test || test_fail
check_sync && test_fail
test_pass

20
test/simulation/131-maxchange Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
. ./test.common
test_start "maxchange directive"
time_offset=2
max_sync_time=5000
client_conf="maxchange 0.1 1 3"
client_step="(* $step (equal 0.1 (sum 1.0) 300))"
run_test || test_fail
check_chronyd_exit && test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync && test_fail
check_log_messages "seconds exceeds.*ignored" 3 3 || test_fail
check_log_messages "seconds exceeds.*exiting" 1 1 || test_fail
test_pass

21
test/simulation/132-logchange Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
. ./test.common
test_start "logchange directive"
time_offset=2
min_sync_time=600
max_sync_time=700
client_server_options="maxsamples 6"
client_conf="logchange 0.1"
client_step="(* $step (equal 0.1 (sum 1.0) 300))"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "clock wrong by" 4 8 || test_fail
test_pass

32
test/simulation/133-hwtimestamp Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
. ./test.common
test_start "hwtimestamp directive"
export CLKNETSIM_TIMESTAMPING=2
refclock_jitter=1e-8
refclock_offset=10.0
min_sync_time=4
max_sync_time=20
limit=200
client_conf="hwtimestamp eth0"
client_server_options="minpoll 0 maxpoll 0 minsamples 32"
client_chronyd_options="-d"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if grep -q 'FEAT_DEBUG 1' ../../config.h; then
check_log_messages "HW clock samples" 190 200 || test_fail
check_log_messages "HW clock reset" 0 0 || test_fail
check_log_messages "Received.*tss=1" 1 1 || test_fail
check_log_messages "Received.*tss=2" 390 400 || test_fail
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
fi
test_pass

View File

@@ -62,7 +62,8 @@ default_client_rpeer_options=""
default_server_conf=""
default_client_conf=""
default_chronyc_conf=""
default_chronyd_options=""
default_server_chronyd_options=""
default_client_chronyd_options=""
default_time_max_limit=1e-3
default_freq_max_limit=5e-4
@@ -75,10 +76,9 @@ default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
# Initialize test settings from their defaults
for defopt in $(declare | grep '^default_'); do
defoptname=${defopt%%=*}
for defoptname in ${!default_*}; do
optname=${defoptname#default_}
eval "[ -z \"\${$optname:+a}\" ] && $optname=\"\$$defoptname\""
[ -z "${!optname}" ] && declare "$optname"="${!defoptname}"
done
test_start() {
@@ -253,7 +253,7 @@ check_chronyd_exit() {
for i in $(seq 1 $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
tail -n 1 tmp/log.$i | grep -q 'chronyd exiting' && \
grep -q 'chronyd exiting' tmp/log.$i && \
! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
@@ -337,6 +337,24 @@ check_chronyc_output() {
return $ret
}
# Check the number of messages matching a matter in the client logs
check_log_messages() {
local i count ret=0 pattern=$1 min=$2 max=$3
test_message 2 1 "checking number of messages \"$pattern\":"
for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
count=$(grep "$pattern" tmp/log.$i | wc -l)
test_message 3 0 "node $i: $count"
[ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check if only NTP port (123) was used
check_packet_port() {
local i ret=0 port=123
@@ -358,14 +376,13 @@ check_packet_port() {
# Print test settings which differ from default value
print_nondefaults() {
local defopt defoptname optname
local defoptname optname
test_message 2 1 "non-default settings:"
declare | grep '^default_*' | while read defopt; do
defoptname=${defopt%%=*}
for defoptname in ${!default_*}; do
optname=${defoptname#default_}
eval "[ \"\$$optname\" = \"\$$defoptname\" ]" || \
test_message 3 1 $(eval "echo $optname=\$$optname")
[ "${!defoptname}" = "${!optname}" ] || \
test_message 3 1 $optname=${!optname}
done
}
@@ -382,7 +399,7 @@ run_simulation() {
}
run_test() {
local i j n stratum node nodes step start freq offset conf
local i j n stratum node nodes step start freq offset conf options
test_message 1 1 "network with $servers*$server_strata servers and $clients clients:"
print_nondefaults
@@ -411,16 +428,19 @@ run_test() {
start=$server_start
freq=""
[ $i -le $falsetickers ] && offset=$i.0 || offset=0.0
options=$server_chronyd_options
elif [ $stratum -le $server_strata ]; then
step=$server_step
start=$server_start
freq=$(get_wander_expr)
offset=0.0
options=$server_chronyd_options
else
step=$client_step
start=$client_start
freq=$(get_wander_expr)
offset=$time_offset
options=$client_chronyd_options
fi
conf=$(get_chronyd_conf $stratum $i $n)
@@ -431,7 +451,7 @@ run_test() {
echo "node${node}_refclock = $(get_refclock_expr)" >> tmp/conf
echo "node${node}_offset = $offset" >> tmp/conf
echo "node${node}_start = $start" >> tmp/conf
start_client $node chronyd "$conf" "" "$chronyd_options" && \
start_client $node chronyd "$conf" "" "$options" && \
test_ok || test_error
[ $? -ne 0 ] && return 1

View File

@@ -65,7 +65,7 @@ test_unit(void)
}
}
DEBUG_LOG("records %d", ARR_GetSize(records));
DEBUG_LOG("records %u", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 64);
for (i = j = 0; i < 10000; i++) {
@@ -76,7 +76,7 @@ test_unit(void)
j++;
}
DEBUG_LOG("requests %u responses %u", i, j);
DEBUG_LOG("requests %d responses %d", i, j);
TEST_CHECK(j * 4 < i && j * 6 > i);
CLG_Finalise();

View File

@@ -106,7 +106,7 @@ test_unit(void)
if (j >= tests[i].length)
TEST_CHECK(length == tests[i].length);
else
TEST_CHECK(length == 0 || length == j || length == tests[i].length);
TEST_CHECK(length == j);
TEST_CHECK(!memcmp(out, tests[i].out, length));
}

View File

@@ -27,56 +27,58 @@ test_unit(void)
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
HCL_Instance clock;
double freq, jitter, interval, dj, sum;
int i, j, count;
int i, j, k, count;
LCL_Initialise();
clock = HCL_CreateInstance(1.0);
for (i = 1; i <= 8; i++) {
clock = HCL_CreateInstance(random() % (1 << i), 1 << i, 1.0);
for (i = 0, count = 0, sum = 0.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);
for (j = 0, count = 0, sum = 0.0; j < 100; j++) {
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("iteration %d", i);
DEBUG_LOG("iteration %d", j);
freq = TST_GetRandomDouble(0.9, 1.1);
jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
interval = TST_GetRandomDouble(0.1, 10.0);
freq = TST_GetRandomDouble(0.9, 1.1);
jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
interval = TST_GetRandomDouble(0.1, 10.0);
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->n_samples = 0;
clock->valid_coefs = 0;
for (j = 0; j < 100; j++) {
UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq, &hw_ts);
UTI_AddDoubleToTimespec(&start_local_ts, j * interval, &local_ts);
if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
DEBUG_LOG("delta/jitter %f", dj);
if (clock->n_samples >= 8)
sum += dj, count++;
TEST_CHECK(clock->n_samples < 4 || dj <= 4.0);
TEST_CHECK(clock->n_samples < 8 || dj <= 3.0);
for (k = 0; k < 100; k++) {
UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq, &hw_ts);
UTI_AddDoubleToTimespec(&start_local_ts, k * interval, &local_ts);
if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
DEBUG_LOG("delta/jitter %f", dj);
if (clock->n_samples >= clock->max_samples / 2)
sum += dj, count++;
TEST_CHECK(clock->n_samples < 4 || dj <= 4.0);
TEST_CHECK(clock->n_samples < 8 || dj <= 3.0);
}
UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
if (HCL_NeedsNewSample(clock, &local_ts))
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
if (!clock->valid_coefs)
continue;
TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
}
UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
if (HCL_NeedsNewSample(clock, &local_ts))
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
if (!clock->valid_coefs)
continue;
TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
}
TEST_CHECK(sum / count < 2.4 / sqrt(clock->max_samples));
HCL_DestroyInstance(clock);
}
TEST_CHECK(sum / count < 0.4);
HCL_DestroyInstance(clock);
LCL_Finalise();
}

View File

@@ -63,6 +63,18 @@ advance_time(double x)
UTI_AddDoubleToTimespec(&current_time, x, &current_time);
}
static uint32_t
get_random_key_id(void)
{
uint32_t id;
do {
id = random() % 6 + 2;
} while (!KEY_KeyKnown(id));
return id;
}
static void
send_request(NCR_Instance inst)
{
@@ -122,6 +134,7 @@ static void
send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth)
{
NTP_Packet *req, *res;
int auth_len = 0;
req = &req_buffer.ntp_pkt;
res = &res_buffer.ntp_pkt;
@@ -168,24 +181,41 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
}
if (authenticated) {
res->auth_keyid = req->auth_keyid;
KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, NTP_NORMAL_PACKET_LENGTH,
res->auth_data, 16);
res_length = NTP_NORMAL_PACKET_LENGTH + 4 + 16;
res->auth_keyid = req->auth_keyid ? req->auth_keyid : htonl(get_random_key_id());
auth_len = KEY_GetAuthLength(ntohl(res->auth_keyid));
assert(auth_len);
if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2)
auth_len = MIN(auth_len, NTP_MAX_V4_MAC_LENGTH - 4);
if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res,
NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len)
assert(0);
res_length = NTP_NORMAL_PACKET_LENGTH + 4 + auth_len;
} else {
res_length = NTP_NORMAL_PACKET_LENGTH;
}
if (!valid_auth) {
switch (random() % 3) {
if (!valid_auth && authenticated) {
assert(auth_len);
switch (random() % 4) {
case 0:
res->auth_keyid++;
res->auth_keyid = htonl(ntohl(res->auth_keyid) + 1);
break;
case 1:
res->auth_data[random() % 16]++;
res->auth_keyid = htonl(ntohl(res->auth_keyid) ^ 1);
if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res,
NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len)
assert(0);
break;
case 2:
res_length = NTP_NORMAL_PACKET_LENGTH;
res->auth_data[random() % auth_len]++;
break;
case 3:
res_length = NTP_NORMAL_PACKET_LENGTH + 4 * (random() % ((4 + auth_len) / 4));
if (NTP_LVM_TO_VERSION(res->lvm) == 4 &&
res_length == NTP_NORMAL_PACKET_LENGTH + NTP_MAX_V4_MAC_LENGTH)
res_length -= 4;
break;
default:
assert(0);
@@ -292,7 +322,10 @@ test_unit(void)
NIO_Initialise(IPADDR_UNSPEC);
NCR_Initialise();
REF_Initialise();
TST_SuspendLogging();
KEY_Initialise();
TST_ResumeLogging();
CNF_SetupAccessRestrictions();
@@ -302,7 +335,7 @@ test_unit(void)
if (random() % 2)
source.params.interleaved = 1;
if (random() % 2)
source.params.authkey = 1;
source.params.authkey = get_random_key_id();
source.params.version = random() % 4 + 1;
UTI_ZeroTimespec(&current_time);

View File

@@ -1,2 +1,6 @@
1 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
2 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
3 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
4 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A
5 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A
6 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F
7 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F

113
test/unit/samplefilt.c Normal file
View File

@@ -0,0 +1,113 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
*/
#include <samplefilt.c>
#include "test.h"
void
test_unit(void)
{
NTP_Sample sample_in, sample_out;
SPF_Instance filter;
int i, j, k, sum_count, min_samples, max_samples;
double mean, combine_ratio, sum_err;
LCL_Initialise();
for (i = 0; i <= 100; i++) {
max_samples = random() % 20 + 1;
min_samples = random() % (max_samples) + 1;
combine_ratio = TST_GetRandomDouble(0.0, 1.0);
filter = SPF_CreateInstance(min_samples, max_samples, 2.0, combine_ratio);
for (j = 0, sum_count = 0, sum_err = 0.0; j < 100; j++) {
DEBUG_LOG("iteration %d/%d", i, j);
mean = TST_GetRandomDouble(-1.0e3, 1.0e3);
UTI_ZeroTimespec(&sample_in.time);
for (k = 0; k < 100; k++) {
UTI_AddDoubleToTimespec(&sample_in.time, TST_GetRandomDouble(1.0e-1, 1.0e2),
&sample_in.time);
sample_in.offset = mean + TST_GetRandomDouble(-1.0, 1.0);
sample_in.peer_dispersion = TST_GetRandomDouble(1.0e-4, 2.0e-4);
sample_in.root_dispersion = TST_GetRandomDouble(1.0e-3, 2.0e-3);
sample_in.peer_delay = TST_GetRandomDouble(1.0e-2, 2.0e-2);
sample_in.root_delay = TST_GetRandomDouble(1.0e-1, 2.0e-1);
sample_in.stratum = random() % 16;
sample_in.leap = random() % 4;
TEST_CHECK(SPF_AccumulateSample(filter, &sample_in));
TEST_CHECK(!SPF_AccumulateSample(filter, &sample_in));
TEST_CHECK(SPF_GetNumberOfSamples(filter) == MIN(k + 1, max_samples));
SPF_GetLastSample(filter, &sample_out);
TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in)));
SPF_SlewSamples(filter, &sample_in.time, 0.0, 0.0);
SPF_AddDispersion(filter, 0.0);
if (k + 1 < min_samples)
TEST_CHECK(!SPF_GetFilteredSample(filter, &sample_out));
TEST_CHECK(SPF_GetNumberOfSamples(filter) == MIN(k + 1, max_samples));
}
if (random() % 10) {
TEST_CHECK(SPF_GetFilteredSample(filter, &sample_out));
TEST_CHECK(SPF_GetAvgSampleDispersion(filter) <= 2.0);
sum_err += sample_out.offset - mean;
sum_count++;
TEST_CHECK(UTI_CompareTimespecs(&sample_out.time, &sample_in.time) <= 0 &&
sample_out.time.tv_sec >= 0);
TEST_CHECK(fabs(sample_out.offset - mean) <= 1.0);
TEST_CHECK(sample_out.peer_dispersion >= 1.0e-4 &&
(sample_out.peer_dispersion <= 2.0e-4 || filter->max_samples > 1));
TEST_CHECK(sample_out.root_dispersion >= 1.0e-3 &&
(sample_out.root_dispersion <= 2.0e-3 || filter->max_samples > 1));
TEST_CHECK(sample_out.peer_delay >= 1.0e-2 &&
sample_out.peer_delay <= 2.0e-2);
TEST_CHECK(sample_out.root_delay >= 1.0e-1 &&
sample_out.root_delay <= 2.0e-1);
TEST_CHECK(sample_out.leap >= 0 && sample_out.leap <= 3);
TEST_CHECK(sample_out.stratum >= 0 && sample_out.stratum <= 15);
if (max_samples == 1)
TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in)));
} else {
SPF_DropSamples(filter);
}
TEST_CHECK(SPF_GetNumberOfSamples(filter) == 0);
}
TEST_CHECK(fabs(sum_err / sum_count) < 0.3);
SPF_DestroyInstance(filter);
}
LCL_Finalise();
}

View File

@@ -26,10 +26,9 @@ test_unit(void)
{
SRC_Instance srcs[16];
RPT_SourceReport report;
NTP_Sample sample;
IPAddr addr;
int i, j, k, l, samples, sel_options;
double offset, delay, disp;
struct timespec ts;
CNF_Initialise(0, 0);
LCL_Initialise();
@@ -59,21 +58,25 @@ test_unit(void)
samples = (i + j) % 5 + 3;
offset = TST_GetRandomDouble(-1.0, 1.0);
sample.offset = TST_GetRandomDouble(-1.0, 1.0);
for (k = 0; k < samples; k++) {
SCH_GetLastEventTime(&ts, NULL, NULL);
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(k - samples, k - samples + 1), &ts);
SCH_GetLastEventTime(&sample.time, NULL, NULL);
UTI_AddDoubleToTimespec(&sample.time, TST_GetRandomDouble(k - samples, k - samples + 1),
&sample.time);
offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
disp = TST_GetRandomDouble(1.0e-6, 1.0e-1);
sample.offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
sample.peer_delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
sample.peer_dispersion = TST_GetRandomDouble(1.0e-6, 1.0e-1);
sample.root_delay = sample.peer_delay;
sample.root_dispersion = sample.peer_dispersion;
sample.stratum = 1;
sample.leap = LEAP_Normal;
DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k,
offset, delay, disp);
sample.offset, sample.peer_delay, sample.peer_dispersion);
SRC_AccumulateSample(srcs[j], &ts, offset, delay, disp, delay, disp,
1, LEAP_Normal);
SRC_AccumulateSample(srcs[j], &sample);
}
for (k = 0; k <= j; k++) {
@@ -82,7 +85,7 @@ test_unit(void)
double passed_lo = DBL_MAX, passed_hi = DBL_MIN;
SRC_SelectSource(srcs[k]);
DEBUG_LOG("source %d status %d", k, sources[k]->status);
DEBUG_LOG("source %d status %u", k, sources[k]->status);
for (l = 0; l <= j; l++) {
TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED);
@@ -125,7 +128,7 @@ test_unit(void)
}
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
SRC_ReportSource(j, &report, &ts);
SRC_ReportSource(j, &report, &sample.time);
SRC_DestroyInstance(srcs[j]);
}
}

View File

@@ -75,6 +75,16 @@ main(int argc, char **argv)
return 0;
}
void TST_SuspendLogging(void)
{
LOG_OpenFileLog("/dev/null");
}
void TST_ResumeLogging(void)
{
LOG_OpenFileLog(NULL);
}
double
TST_GetRandomDouble(double min, double max)
{

View File

@@ -35,6 +35,9 @@ extern void test_unit(void);
extern void TST_Fail(int line);
extern void TST_SuspendLogging(void);
extern void TST_ResumeLogging(void);
extern double TST_GetRandomDouble(double min, double max);
extern void TST_GetRandomAddress(IPAddr *ip, int family, int bits);
extern void TST_SwapAddressBit(IPAddr *ip, unsigned int b);

View File

@@ -3,13 +3,17 @@
void test_unit(void) {
NTP_int64 ntp_ts, ntp_fuzz;
NTP_int32 ntp32_ts;
struct timespec ts, ts2;
struct timeval tv;
struct sockaddr_un sun;
double x, y;
double x, y, nan, inf;
Timespec tspec;
Float f;
int i, j, c;
char buf[16], *s;
uid_t uid;
gid_t gid;
for (i = -31; i < 31; i++) {
x = pow(2.0, i);
@@ -33,6 +37,11 @@ void test_unit(void) {
TEST_CHECK(x > 0.0 || x <= 0.0);
}
for (i = 0; i < 100000; i++) {
UTI_GetRandomBytes(&ntp32_ts, sizeof (ntp32_ts));
TEST_CHECK(UTI_DoubleToNtp32(UTI_Ntp32ToDouble(ntp32_ts)) == ntp32_ts);
}
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));
@@ -175,6 +184,23 @@ void test_unit(void) {
TEST_CHECK(c > 400 && c < 600);
}
ts.tv_nsec = 0;
ts.tv_sec = 10;
TEST_CHECK(!UTI_IsTimeOffsetSane(&ts, -20.0));
#ifdef HAVE_LONG_TIME_T
ts.tv_sec = NTP_ERA_SPLIT + (1LL << 32);
#else
ts.tv_sec = 0x7fffffff - MIN_ENDOFTIME_DISTANCE;
#endif
TEST_CHECK(!UTI_IsTimeOffsetSane(&ts, 10.0));
TEST_CHECK(UTI_IsTimeOffsetSane(&ts, -20.0));
UTI_TimespecHostToNetwork(&ts, &tspec);
UTI_TimespecNetworkToHost(&tspec, &ts2);
TEST_CHECK(!UTI_CompareTimespecs(&ts, &ts2));
for (i = c = 0; i < 100000; i++) {
j = random() % (sizeof (buf) + 1);
UTI_GetRandomBytes(buf, j);
@@ -196,4 +222,46 @@ void test_unit(void) {
TEST_CHECK(s[BUFFER_LENGTH - 2] == '>');
}
}
s = UTI_PathToDir("/aaa/bbb/ccc/ddd");
TEST_CHECK(!strcmp(s, "/aaa/bbb/ccc"));
Free(s);
s = UTI_PathToDir("aaa");
TEST_CHECK(!strcmp(s, "."));
Free(s);
s = UTI_PathToDir("/aaaa");
TEST_CHECK(!strcmp(s, "/"));
Free(s);
nan = strtod("nan", NULL);
inf = strtod("inf", NULL);
TEST_CHECK(MIN(2.0, -1.0) == -1.0);
TEST_CHECK(MIN(-1.0, 2.0) == -1.0);
TEST_CHECK(MIN(inf, 2.0) == 2.0);
TEST_CHECK(MAX(2.0, -1.0) == 2.0);
TEST_CHECK(MAX(-1.0, 2.0) == 2.0);
TEST_CHECK(MAX(inf, 2.0) == inf);
TEST_CHECK(CLAMP(1.0, -1.0, 2.0) == 1.0);
TEST_CHECK(CLAMP(1.0, 3.0, 2.0) == 2.0);
TEST_CHECK(CLAMP(1.0, inf, 2.0) == 2.0);
TEST_CHECK(CLAMP(1.0, nan, 2.0) == 2.0);
TEST_CHECK(SQUARE(3.0) == 3.0 * 3.0);
rmdir("testdir");
uid = geteuid();
gid = getegid();
TEST_CHECK(UTI_CreateDirAndParents("testdir", 0700, uid, gid));
TST_SuspendLogging();
TEST_CHECK(UTI_CheckDirPermissions("testdir", 0700, uid, gid));
TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0300, uid, gid));
TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid + 1, gid));
TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid, gid + 1));
TST_ResumeLogging();
}

41
util.c
View File

@@ -298,16 +298,17 @@ UTI_IPToString(IPAddr *addr)
b = (ip>>16) & 0xff;
c = (ip>> 8) & 0xff;
d = (ip>> 0) & 0xff;
snprintf(result, BUFFER_LENGTH, "%ld.%ld.%ld.%ld", a, b, c, d);
snprintf(result, BUFFER_LENGTH, "%lu.%lu.%lu.%lu", a, b, c, d);
break;
case IPADDR_INET6:
ip6 = addr->addr.in6;
#ifdef FEAT_IPV6
inet_ntop(AF_INET6, ip6, result, BUFFER_LENGTH);
#else
snprintf(result, BUFFER_LENGTH, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7],
ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15]);
assert(BUFFER_LENGTH >= 40);
for (a = 0; a < 8; a++)
snprintf(result + a * 5, 40 - a * 5, "%04x:",
(unsigned int)(ip6[2 * a] << 8 | ip6[2 * a + 1]));
#endif
break;
default:
@@ -994,34 +995,38 @@ UTI_FdSetCloexec(int fd)
/* ================================================== */
int
UTI_SetQuitSignalsHandler(void (*handler)(int))
void
UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe)
{
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = SA_RESTART;
if (sigemptyset(&sa.sa_mask) < 0)
return 0;
LOG_FATAL("sigemptyset() failed");
#ifdef SIGINT
if (sigaction(SIGINT, &sa, NULL) < 0)
return 0;
LOG_FATAL("sigaction(%d) failed", SIGINT);
#endif
#ifdef SIGTERM
if (sigaction(SIGTERM, &sa, NULL) < 0)
return 0;
LOG_FATAL("sigaction(%d) failed", SIGTERM);
#endif
#ifdef SIGQUIT
if (sigaction(SIGQUIT, &sa, NULL) < 0)
return 0;
LOG_FATAL("sigaction(%d) failed", SIGQUIT);
#endif
#ifdef SIGHUP
if (sigaction(SIGHUP, &sa, NULL) < 0)
return 0;
LOG_FATAL("sigaction(%d) failed", SIGHUP);
#endif
return 1;
if (ignore_sigpipe)
sa.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sa, NULL) < 0)
LOG_FATAL("sigaction(%d) failed", SIGPIPE);
}
/* ================================================== */
@@ -1160,12 +1165,12 @@ UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
}
if (buf.st_uid != uid) {
LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "UID", uid);
LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "UID", uid);
return 0;
}
if (buf.st_gid != gid) {
LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "GID", gid);
LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "GID", gid);
return 0;
}
@@ -1183,13 +1188,13 @@ UTI_DropRoot(uid_t uid, gid_t gid)
/* Set effective, saved and real group ID */
if (setgid(gid))
LOG_FATAL("setgid(%d) failed : %s", gid, strerror(errno));
LOG_FATAL("setgid(%u) failed : %s", gid, strerror(errno));
/* Set effective, saved and real user ID */
if (setuid(uid))
LOG_FATAL("setuid(%d) failed : %s", uid, strerror(errno));
LOG_FATAL("setuid(%u) failed : %s", uid, strerror(errno));
DEBUG_LOG("Dropped root privileges: UID %d GID %d", uid, gid);
DEBUG_LOG("Dropped root privileges: UID %u GID %u", uid, gid);
}
/* ================================================== */
@@ -1224,7 +1229,7 @@ get_random_bytes_getrandom(char *buf, unsigned int len)
if (disabled)
break;
if (getrandom(rand_buf, sizeof (rand_buf), 0) != sizeof (rand_buf)) {
if (getrandom(rand_buf, sizeof (rand_buf), GRND_NONBLOCK) != sizeof (rand_buf)) {
disabled = 1;
break;
}

4
util.h
View File

@@ -161,7 +161,7 @@ extern Float UTI_FloatHostToNetwork(double x);
/* Set FD_CLOEXEC on descriptor */
extern int UTI_FdSetCloexec(int fd);
extern int UTI_SetQuitSignalsHandler(void (*handler)(int));
extern void UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe);
/* Get directory (as an allocated string) for a path */
extern char *UTI_PathToDir(const char *path);
@@ -200,4 +200,6 @@ extern void UTI_GetRandomBytes(void *buf, unsigned int len);
/* Macro to clamp a value between two values */
#define CLAMP(min, x, max) (MAX((min), MIN((x), (max))))
#define SQUARE(x) ((x) * (x))
#endif /* GOT_UTIL_H */