mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-03 18:35:06 -05:00
Compare commits
167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04328ceead | ||
|
|
f00fed2009 | ||
|
|
ffb9887cce | ||
|
|
9220c9b8a2 | ||
|
|
2e28b19112 | ||
|
|
636a4e2794 | ||
|
|
5c9e1e0b69 | ||
|
|
64fd1b8ba5 | ||
|
|
69d3913f3e | ||
|
|
08fd011b6a | ||
|
|
c172268cfe | ||
|
|
94b014865c | ||
|
|
099aaf2cb1 | ||
|
|
4481a8b24f | ||
|
|
b626fe661e | ||
|
|
ba8fcd145d | ||
|
|
981d09de40 | ||
|
|
86a99bb257 | ||
|
|
3093a11cd0 | ||
|
|
058b788d38 | ||
|
|
66a42fa493 | ||
|
|
a85f63cc15 | ||
|
|
bbe1e69dcc | ||
|
|
1b52bba7b9 | ||
|
|
c5c80ef400 | ||
|
|
a78031ce0d | ||
|
|
34e9dd13ce | ||
|
|
6e52a9be7a | ||
|
|
69c6dffd63 | ||
|
|
2ddd0ae231 | ||
|
|
79db0b7eca | ||
|
|
2ebba7fbaa | ||
|
|
e392d1fde9 | ||
|
|
d7c93ec950 | ||
|
|
6af39d63aa | ||
|
|
cc8414b1b3 | ||
|
|
6b44055e3d | ||
|
|
9f9c6cc6ab | ||
|
|
f176193d35 | ||
|
|
e8bc41e862 | ||
|
|
91dbe3c6c2 | ||
|
|
3e876d4218 | ||
|
|
31b1f2e8a1 | ||
|
|
4169e94b1d | ||
|
|
948ecf8431 | ||
|
|
91f3f97ea7 | ||
|
|
65bb65b440 | ||
|
|
ea6e8d85a3 | ||
|
|
add932501f | ||
|
|
89390a738f | ||
|
|
ac4f6ab93b | ||
|
|
dbcb1b9b0b | ||
|
|
6375307798 | ||
|
|
fb78e60d26 | ||
|
|
b822c7164f | ||
|
|
aa295730a0 | ||
|
|
69d70703b2 | ||
|
|
b2b6ef00dc | ||
|
|
5dc86c236b | ||
|
|
2563dd9d29 | ||
|
|
a899e3df33 | ||
|
|
692cea49f8 | ||
|
|
bcedacaa3d | ||
|
|
be3c1b5243 | ||
|
|
e626ec6c37 | ||
|
|
49d52b547f | ||
|
|
74a546a9e7 | ||
|
|
d1777087c1 | ||
|
|
cf7b5363cd | ||
|
|
7f3183cc72 | ||
|
|
f1b8da085b | ||
|
|
09dfca49ec | ||
|
|
88e0ec07aa | ||
|
|
0adc8e8f92 | ||
|
|
5fc7674e36 | ||
|
|
018977044a | ||
|
|
cc49d8e6e6 | ||
|
|
933bd017b4 | ||
|
|
d558b33d85 | ||
|
|
9268bf2cff | ||
|
|
dbf2c22467 | ||
|
|
af4fe92095 | ||
|
|
e034a07be8 | ||
|
|
eb8c9ad601 | ||
|
|
6847536669 | ||
|
|
f5206db9b0 | ||
|
|
6ab2ed0da6 | ||
|
|
7352e470e1 | ||
|
|
5bc9c0d07a | ||
|
|
a2146e82ef | ||
|
|
6e10e6740c | ||
|
|
bfaa10f2b0 | ||
|
|
9f167a7997 | ||
|
|
6908163464 | ||
|
|
671daf06b8 | ||
|
|
b189a5386b | ||
|
|
7889d108c2 | ||
|
|
3cfa8ce9d3 | ||
|
|
570573fe28 | ||
|
|
62b1a11736 | ||
|
|
c00d517e12 | ||
|
|
001f3d5e27 | ||
|
|
6045023a49 | ||
|
|
bba29a0ee7 | ||
|
|
cffc856b50 | ||
|
|
419077e04b | ||
|
|
7db9d4acea | ||
|
|
8d5b86efe7 | ||
|
|
6cf16aea7b | ||
|
|
870545d3cb | ||
|
|
2a030c0d0c | ||
|
|
0b709ab1bc | ||
|
|
a1f2f17385 | ||
|
|
2240eefbd0 | ||
|
|
706d0c281a | ||
|
|
ca73e34f30 | ||
|
|
cca2ef4649 | ||
|
|
05d9edbf8f | ||
|
|
c5bdc52a59 | ||
|
|
74f0c0924a | ||
|
|
05492d1d23 | ||
|
|
eea343b93f | ||
|
|
afff06c88c | ||
|
|
c0717a27f6 | ||
|
|
159bd73f76 | ||
|
|
9931a9166b | ||
|
|
8aa4ae027b | ||
|
|
dcce79fdbe | ||
|
|
189aafde9d | ||
|
|
99e3045df4 | ||
|
|
c498c21fad | ||
|
|
6bef8aa0e9 | ||
|
|
108d112272 | ||
|
|
05078e4252 | ||
|
|
4ceb9e4cd0 | ||
|
|
a9f237a395 | ||
|
|
e7ca560c3d | ||
|
|
d9f86f6f70 | ||
|
|
879d936277 | ||
|
|
5bb2bf9361 | ||
|
|
a8167b7959 | ||
|
|
b33b682356 | ||
|
|
2c47602c33 | ||
|
|
59d1b41716 | ||
|
|
5b75d4afef | ||
|
|
e15c7cd236 | ||
|
|
9bc774d6af | ||
|
|
9b34556952 | ||
|
|
9a6369d8f1 | ||
|
|
49cdd6bf09 | ||
|
|
63fe34e890 | ||
|
|
85465afb62 | ||
|
|
339cb06a49 | ||
|
|
10150bfcab | ||
|
|
e50dc739d8 | ||
|
|
26e08abe71 | ||
|
|
7637faa0d0 | ||
|
|
8a57a28177 | ||
|
|
34db671b57 | ||
|
|
8b9021bf34 | ||
|
|
ce6b896948 | ||
|
|
2962fc6286 | ||
|
|
76bed76289 | ||
|
|
113f2ebec0 | ||
|
|
7c5bd948bb | ||
|
|
8cbc68f28f | ||
|
|
bf7aa52394 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,9 +1,12 @@
|
||||
.deps
|
||||
.vimrc
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.o
|
||||
*.swp
|
||||
*.dSYM
|
||||
*.DS_Store
|
||||
core.*
|
||||
tags
|
||||
/RELEASES
|
||||
/Makefile
|
||||
|
||||
10
Makefile.in
10
Makefile.in
@@ -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@
|
||||
@@ -70,7 +70,8 @@ distclean : clean
|
||||
-rm -f Makefile config.h config.log
|
||||
|
||||
clean :
|
||||
-rm -f *.o *.s chronyc chronyd core *~
|
||||
-rm -f *.o *.s chronyc chronyd core.* *~
|
||||
-rm -f *.gcda *.gcno
|
||||
-rm -rf .deps
|
||||
-rm -rf *.dSYM
|
||||
|
||||
@@ -112,10 +113,15 @@ install-docs :
|
||||
quickcheck : chronyd chronyc
|
||||
$(MAKE) -C test/unit check
|
||||
cd test/simulation && ./run
|
||||
cd test/system && ./run
|
||||
|
||||
check : chronyd chronyc
|
||||
$(MAKE) -C test/unit check
|
||||
cd test/simulation && ./run -i 20 -m 2
|
||||
cd test/system && ./run
|
||||
|
||||
print-chronyd-objects :
|
||||
@echo $(OBJS) $(EXTRA_OBJS)
|
||||
|
||||
Makefile : Makefile.in configure
|
||||
@echo
|
||||
|
||||
51
NEWS
51
NEWS
@@ -1,3 +1,54 @@
|
||||
New in version 3.5.1
|
||||
====================
|
||||
|
||||
Security fixes
|
||||
--------------
|
||||
* Create new file when writing pidfile (CVE-2020-14367)
|
||||
|
||||
New in version 3.5
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add support for more accurate reading of PHC on Linux 5.0
|
||||
* Add support for hardware timestamping on interfaces with read-only
|
||||
timestamping configuration
|
||||
* Add support for memory locking and real-time priority on FreeBSD,
|
||||
NetBSD, Solaris
|
||||
* Update seccomp filter to work on more architectures
|
||||
* Validate refclock driver options
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Fix bindaddress directive on FreeBSD
|
||||
* Fix transposition of hardware RX timestamp on Linux 4.13 and later
|
||||
* Fix building on non-glibc systems
|
||||
|
||||
New in version 3.4
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add filter option to server/pool/peer directive
|
||||
* Add minsamples and maxsamples options to hwtimestamp directive
|
||||
* Add support for faster frequency adjustments in Linux 4.19
|
||||
* Change default pidfile to /var/run/chrony/chronyd.pid to allow
|
||||
chronyd without root privileges to remove it on exit
|
||||
* Disable sub-second polling intervals for distant NTP sources
|
||||
* Extend range of supported sub-second polling intervals
|
||||
* Get/set IPv4 destination/source address of NTP packets on FreeBSD
|
||||
* Make burst options and command useful with short polling intervals
|
||||
* Modify auto_offline option to activate when sending request failed
|
||||
* Respond from interface that received NTP request if possible
|
||||
* Add onoffline command to switch between online and offline state
|
||||
according to current system network configuration
|
||||
* Improve example NetworkManager dispatcher script
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Avoid waiting in Linux getrandom system call
|
||||
* Fix PPS support on FreeBSD and NetBSD
|
||||
|
||||
New in version 3.3
|
||||
==================
|
||||
|
||||
|
||||
109
README
109
README
@@ -108,132 +108,57 @@ The following people have provided patches and other major contributions
|
||||
to the program :
|
||||
|
||||
Lonnie Abelbeck <lonnie@abelbeck.com>
|
||||
Patch to add tab-completion to chronyc
|
||||
|
||||
Benny Lyne Amorsen <benny@amorsen.dk>
|
||||
Patch to add minstratum option
|
||||
|
||||
Andrew Bishop <amb@gedanken.demon.co.uk>
|
||||
Fixes for bugs in logging when in daemon mode
|
||||
Fixes for compiler warnings
|
||||
Robustness improvements for drift file
|
||||
Improve installation (directory checking etc)
|
||||
Entries in contrib directory
|
||||
Improvements to 'sources' and 'sourcestats' output from chronyc
|
||||
Improvements to documentation
|
||||
Investigation of required dosynctodr behaviour for various Solaris
|
||||
versions
|
||||
|
||||
Vincent Blut <vincent.debian@free.fr>
|
||||
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
|
||||
Entries in contrib directory
|
||||
|
||||
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
|
||||
Leigh Brown <leigh@solinno.co.uk>
|
||||
Erik Bryer <ebryer@spots.ab.ca>
|
||||
Entries in contrib directory
|
||||
|
||||
Jonathan Cameron <jic23@cam.ac.uk>
|
||||
Bryan Christianson <bryan@whatroute.net>
|
||||
Support for macOS
|
||||
Support for privilege separation
|
||||
Entries in contrib directory
|
||||
|
||||
Juliusz Chroboczek <jch@pps.jussieu.fr>
|
||||
Patch to fix install rule in Makefile if chronyd file is in use
|
||||
|
||||
Christian Ehrhardt <christian.ehrhardt@canonical.com>
|
||||
Patch to generate a warning message when CAP_SYS_TIME is missing
|
||||
|
||||
Paul Elliott <pelliott@io.com>
|
||||
Entries in contrib directory
|
||||
|
||||
Stefan R. Filipek <srfilipek@gmail.com>
|
||||
Mike Fleetwood <mike@rockover.demon.co.uk>
|
||||
Fixes for compiler warnings
|
||||
|
||||
Alexander Gretencord <arutha@gmx.de>
|
||||
Changes to installation directory system to make it easier for
|
||||
package builders
|
||||
|
||||
Andrew Griffiths <agriffit@redhat.com>
|
||||
Patch to add support for seccomp filter
|
||||
|
||||
Walter Haidinger <walter.haidinger@gmx.at>
|
||||
Access to a Linux installation where v1.12 wouldn't compile
|
||||
Disc space for an independent backup of the sources
|
||||
|
||||
Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
|
||||
Port to NetBSD
|
||||
|
||||
John Hasler <john@dhh.gt.org>
|
||||
Project and website at tuxfamily.org
|
||||
Changes to support 64 bit machines (i.e. those where
|
||||
sizeof(unsigned long) > 4)
|
||||
Bug fix to initstepslew directive
|
||||
Fix to remove potential buffer overrun errors
|
||||
Memory locking and real-time scheduler support
|
||||
Fix fault where chronyd enters an endless loop
|
||||
|
||||
Tjalling Hattink <t.hattink@fugro.nl>
|
||||
Fix scheduler to allow stepping clock from timeout handler
|
||||
Patch to take leap second in PPS refclock from locked source
|
||||
Patch to make reading of RTC for initial trim more reliable
|
||||
|
||||
Liam Hatton <me@liamhatton.com>
|
||||
Advice on configuring for Linux on PPC
|
||||
|
||||
Jachym Holecek <jakym@volny.cz>
|
||||
Patch to make Linux real time clock work with devfs
|
||||
|
||||
Håkan Johansson <f96hajo@chalmers.se>
|
||||
Patch to avoid large values in sources and sourcestats output
|
||||
|
||||
Jim Knoble <jmknoble@pobox.com>
|
||||
Fixes for compiler warnings
|
||||
|
||||
Antti Jrvinen <costello@iki.fi>
|
||||
Advice on configuring for BSD/386
|
||||
|
||||
Eric Lammerts <eric@lammerts.org>
|
||||
Stefan Lucke <stefan@lucke.in-berlin.de>
|
||||
Victor Lum <viclum@vanu.com>
|
||||
Kevin Lyda <kevin@ie.suberic.net>
|
||||
Paul Menzel <paulepanter@users.sourceforge.net>
|
||||
Vladimir Michl <vladimir.michl@seznam.cz>
|
||||
Victor Moroz <vim@prv.adlum.ru>
|
||||
Patch to support Linux with HZ!=100
|
||||
|
||||
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
|
||||
Patch to add acquisitionport directive
|
||||
|
||||
Frank Otto <sandwichmacher@web.de>
|
||||
Handling arbitrary HZ values
|
||||
|
||||
Denny Page <dennypage@me.com>
|
||||
Advice on support for hardware timestamping
|
||||
|
||||
Chris Perl <cperl@janestreet.com>
|
||||
Patches to improve support for refclocks keeping time in TAI
|
||||
|
||||
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
|
||||
Patch to add refresh command to chronyc
|
||||
|
||||
Andreas Piesk <apiesk@virbus.de>
|
||||
Patch to make chronyc use the readline library if available
|
||||
|
||||
Andreas Steinmetz <ast@domdv.de>
|
||||
Patch to make stratum of refclocks configurable
|
||||
|
||||
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
|
||||
Timo Teras <timo.teras@iki.fi>
|
||||
Patch to reply correctly on multihomed hosts
|
||||
|
||||
Bill Unruh <unruh@physics.ubc.ca>
|
||||
Advice on statistics
|
||||
|
||||
Stephen Wadeley <swadeley@redhat.com>
|
||||
Improvements to man pages
|
||||
|
||||
Bernhard Weiss <lisnablagh@web.de>
|
||||
Wolfgang Weisselberg <weissel@netcologne.de>
|
||||
Entries in contrib directory
|
||||
|
||||
Bernhard M. Wiedemann <bwiedemann@suse.de>
|
||||
Joachim Wiedorn <ad_debian@joonet.de>
|
||||
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
|
||||
Many robustness and security improvements
|
||||
|
||||
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
|
||||
Information about the Linux 2.2 kernel functionality compared to 2.0
|
||||
|
||||
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
|
||||
Doug Woodward <dougw@whistler.com>
|
||||
Advice on configuring for Solaris 2.8 on x86
|
||||
Thomas Zajic <zlatko@zlatko.fdns.net>
|
||||
|
||||
Many other people have contributed bug reports and suggestions. We are sorry
|
||||
we cannot identify all of you individually.
|
||||
|
||||
6
candm.h
6
candm.h
@@ -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;
|
||||
|
||||
|
||||
89
client.c
89
client.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Lonnie Abelbeck 2016
|
||||
* Copyright (C) Lonnie Abelbeck 2016, 2018
|
||||
* Copyright (C) Miroslav Lichvar 2009-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -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")) {
|
||||
@@ -3106,7 +3159,7 @@ static void
|
||||
display_gpl(void)
|
||||
{
|
||||
printf("chrony version %s\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2018 Richard P. Curnow and others\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2019 Richard P. Curnow and others\n"
|
||||
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
|
||||
"you are welcome to redistribute it under certain conditions. See the\n"
|
||||
"GNU General Public License version 2 for details.\n\n",
|
||||
@@ -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);
|
||||
|
||||
|
||||
27
cmdmon.c
27
cmdmon.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -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);
|
||||
|
||||
@@ -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
12
conf.c
@@ -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
2
conf.h
@@ -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;
|
||||
|
||||
64
configure
vendored
64
configure
vendored
@@ -6,6 +6,7 @@
|
||||
# Copyright (C) Richard P. Curnow 1997-2003
|
||||
# Copyright (C) Bryan Christianson 2016
|
||||
# Copyright (C) Miroslav Lichvar 2009, 2012-2018
|
||||
# Copyright (C) Stefan R. Filipek 2019
|
||||
#
|
||||
# =======================================================================
|
||||
|
||||
@@ -108,7 +109,7 @@ For better control, use the options below.
|
||||
since 1970-01-01 [50*365 days ago]
|
||||
--with-user=USER Specify default chronyd user [root]
|
||||
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
|
||||
--with-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
|
||||
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
|
||||
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
|
||||
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
|
||||
--enable-debug Enable debugging support
|
||||
@@ -227,9 +228,10 @@ feat_timestamping=1
|
||||
try_timestamping=0
|
||||
feat_ntp_signd=0
|
||||
ntp_era_split=""
|
||||
use_pthread=0
|
||||
default_user="root"
|
||||
default_hwclockfile=""
|
||||
default_pidfile="/var/run/chronyd.pid"
|
||||
default_pidfile="/var/run/chrony/chronyd.pid"
|
||||
default_rtcdevice="/dev/rtc"
|
||||
mail_program="/usr/lib/sendmail"
|
||||
|
||||
@@ -395,7 +397,7 @@ SYSTEM=${OPERATINGSYSTEM}-${MACHINE}
|
||||
|
||||
case $OPERATINGSYSTEM in
|
||||
Linux)
|
||||
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o sys_posix.o"
|
||||
[ $try_libcap != "0" ] && try_libcap=1
|
||||
try_rtc=1
|
||||
[ $try_seccomp != "0" ] && try_seccomp=1
|
||||
@@ -410,7 +412,9 @@ case $OPERATINGSYSTEM in
|
||||
# recvmmsg() seems to be broken on FreeBSD 11.0 and it's just
|
||||
# a wrapper around recvmsg()
|
||||
try_recvmmsg=0
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
add_def FREEBSD
|
||||
if [ $feat_droproot = "1" ]; then
|
||||
add_def FEAT_PRIVDROP
|
||||
@@ -419,8 +423,10 @@ case $OPERATINGSYSTEM in
|
||||
echo "Configuring for $SYSTEM"
|
||||
;;
|
||||
NetBSD)
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
|
||||
try_clockctl=1
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
add_def NETBSD
|
||||
echo "Configuring for $SYSTEM"
|
||||
;;
|
||||
@@ -445,9 +451,11 @@ case $OPERATINGSYSTEM in
|
||||
echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
|
||||
;;
|
||||
SunOS)
|
||||
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
|
||||
EXTRA_LIBS="-lsocket -lnsl -lresolv"
|
||||
EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
add_def SOLARIS
|
||||
# These are needed to have msg_control in struct msghdr
|
||||
add_def __EXTENSIONS__
|
||||
@@ -595,14 +603,6 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
if test_code '<stdint.h>' 'stdint.h' '' '' ''; then
|
||||
add_def HAVE_STDINT_H
|
||||
fi
|
||||
|
||||
if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
|
||||
add_def HAVE_INTTYPES_H
|
||||
fi
|
||||
|
||||
if test_code 'struct in_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
|
||||
struct in_pktinfo ipi;
|
||||
return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;'
|
||||
@@ -655,12 +655,12 @@ fi
|
||||
|
||||
if [ $feat_asyncdns = "1" ] && \
|
||||
test_code 'pthread' 'pthread.h' '-pthread' '' \
|
||||
'return pthread_create((void *)1, NULL, (void *)1, NULL);'
|
||||
'return (int)pthread_create((void *)1, NULL, (void *)1, NULL);'
|
||||
then
|
||||
add_def FEAT_ASYNCDNS
|
||||
add_def USE_PTHREAD_ASYNCDNS
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
|
||||
MYCFLAGS="$MYCFLAGS -pthread"
|
||||
use_pthread=1
|
||||
fi
|
||||
|
||||
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
|
||||
@@ -715,11 +715,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 +727,7 @@ if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
|
||||
fi
|
||||
|
||||
if [ "x$timepps_h" != "x" ] && \
|
||||
test_code 'PPSAPI' "string.h $timepps_h" '' '' '
|
||||
test_code 'PPSAPI' "inttypes.h string.h time.h $timepps_h" '' '' '
|
||||
pps_handle_t h = 0;
|
||||
pps_info_t i;
|
||||
struct timespec ts;
|
||||
@@ -794,25 +794,33 @@ fi
|
||||
|
||||
if [ $try_setsched = "1" ] && \
|
||||
test_code \
|
||||
'sched_setscheduler()' \
|
||||
'sched.h' '' '' '
|
||||
'pthread_setschedparam()' \
|
||||
'pthread.h sched.h' '-pthread' '' '
|
||||
struct sched_param sched;
|
||||
sched_get_priority_max(SCHED_FIFO);
|
||||
sched_setscheduler(0, SCHED_FIFO, &sched);'
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
|
||||
then
|
||||
add_def HAVE_SCHED_SETSCHEDULER
|
||||
add_def HAVE_PTHREAD_SETSCHEDPARAM
|
||||
use_pthread=1
|
||||
fi
|
||||
|
||||
if [ $try_lockmem = "1" ] && \
|
||||
test_code \
|
||||
'mlockall()' \
|
||||
'sys/mman.h sys/resource.h' '' '' '
|
||||
struct rlimit rlim;
|
||||
setrlimit(RLIMIT_MEMLOCK, &rlim);
|
||||
'sys/mman.h' '' '' '
|
||||
mlockall(MCL_CURRENT|MCL_FUTURE);'
|
||||
then
|
||||
add_def HAVE_MLOCKALL
|
||||
fi
|
||||
if [ $try_lockmem = "1" ] && \
|
||||
test_code \
|
||||
'setrlimit(RLIMIT_MEMLOCK, ...)' \
|
||||
'sys/resource.h' '' '' '
|
||||
struct rlimit rlim;
|
||||
setrlimit(RLIMIT_MEMLOCK, &rlim);'
|
||||
then
|
||||
add_def HAVE_SETRLIMIT_MEMLOCK
|
||||
fi
|
||||
|
||||
if [ $feat_forcednsretry = "1" ]
|
||||
then
|
||||
@@ -904,6 +912,10 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $use_pthread = "1" ]; then
|
||||
MYCFLAGS="$MYCFLAGS -pthread"
|
||||
fi
|
||||
|
||||
SYSCONFDIR=/etc
|
||||
if [ "x$SETSYSCONFDIR" != "x" ]; then
|
||||
SYSCONFDIR=$SETSYSCONFDIR
|
||||
|
||||
@@ -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*:::
|
||||
@@ -367,12 +374,12 @@ for *initstepslew* to finish before exiting. This is useful to prevent programs
|
||||
started in the boot sequence after *chronyd* from reading the clock before it
|
||||
has been stepped.
|
||||
|
||||
[[refclock]]*refclock* _driver_ _parameter_[:__option__,...] [_option_]...::
|
||||
[[refclock]]*refclock* _driver_ _parameter_[:__option__]... [_option_]...::
|
||||
The *refclock* directive specifies a hardware reference clock to be used as a
|
||||
time source. It has two mandatory parameters, a driver name and a
|
||||
driver-specific parameter. The two parameters are followed by zero or more
|
||||
refclock options. Some drivers have special options, which can be appended to
|
||||
the driver-specific parameter (separated by the *:* and *,* characters).
|
||||
the driver-specific parameter using the *:* character.
|
||||
+
|
||||
There are four drivers included in *chronyd*:
|
||||
+
|
||||
@@ -469,7 +476,7 @@ Examples:
|
||||
----
|
||||
refclock PHC /dev/ptp0 poll 0 dpoll -2 offset -37
|
||||
refclock PHC /dev/ptp1:nocrossts poll 3 pps
|
||||
refclock PHC /dev/ptp2:extpps,pin=1 width 0.2 poll 2
|
||||
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
|
||||
----
|
||||
+
|
||||
::
|
||||
@@ -654,6 +661,13 @@ The *minsamples* directive sets the default minimum number of samples that
|
||||
*chronyd* should keep for each source. This setting can be overridden for
|
||||
individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
|
||||
directives. The default value is 6. The useful range is 4 to 64.
|
||||
+
|
||||
Forcing *chronyd* to keep more samples than it would normally keep reduces
|
||||
noise in the estimated frequency and offset, but slows down the response to
|
||||
changes in the frequency and offset of the clock. The offsets in the
|
||||
<<chronyc.adoc#tracking,*tracking*>> and
|
||||
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
|
||||
_statistics.log_ files) may be smaller than the actual offsets.
|
||||
|
||||
=== Source selection
|
||||
|
||||
@@ -667,6 +681,8 @@ The *combinelimit* directive limits which sources are included in the combining
|
||||
algorithm. Their synchronisation distance has to be shorter than the distance
|
||||
of the selected source multiplied by the value of the limit. Also, their
|
||||
measured frequencies have to be close to the frequency of the selected source.
|
||||
If the selected source was specified with the *prefer* option, it can be
|
||||
combined only with other sources specified with this option.
|
||||
+
|
||||
By default, the limit is 3. Setting the limit to 0 effectively disables the
|
||||
source combining algorithm and only the selected source will be used to control
|
||||
@@ -1587,8 +1603,8 @@ If the *rtconutc* directive appears, it means the RTC is required to keep UTC.
|
||||
The directive takes no arguments. It is equivalent to specifying the *-u*
|
||||
switch to the Linux *hwclock* program.
|
||||
+
|
||||
Note that this setting is overridden when the <<hwclockfile,*hwclockfile*>>
|
||||
directive is specified.
|
||||
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
|
||||
and is not relevant for the <<rtcsync,*rtcsync*>> directive.
|
||||
|
||||
[[rtcsync]]*rtcsync*::
|
||||
The *rtcsync* directive enables a mode where the system time is periodically
|
||||
@@ -1938,6 +1954,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).
|
||||
@@ -2036,46 +2058,51 @@ that has password shorter than 80 bits.
|
||||
The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
|
||||
generate random keys for the key file. By default, it generates 160-bit MD5 or
|
||||
SHA1 keys.
|
||||
+
|
||||
For security reasons, the file should be readable only by root and the user
|
||||
under which *chronyd* is normally running (to allow *chronyd* to re-read the
|
||||
file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
|
||||
|
||||
[[lock_all]]*lock_all*::
|
||||
The *lock_all* directive will lock chronyd into RAM so that it will never be
|
||||
paged out. This mode is only supported on Linux. This directive uses the Linux
|
||||
*mlockall()* system call to prevent *chronyd* from ever being swapped out. This
|
||||
should result in lower and more consistent latency. It should not have
|
||||
significant impact on performance as *chronyd's* memory usage is modest. The
|
||||
*mlockall(2)* man page has more details.
|
||||
paged out. This mode is supported on Linux, FreeBSD, NetBSD, and Solaris. This
|
||||
directive uses the POSIX *mlockall()* system call to prevent *chronyd* from
|
||||
ever being swapped out. This should result in lower and more consistent
|
||||
latency. It should not have significant impact on performance as *chronyd's*
|
||||
memory usage is modest. The *mlockall(2)* man page has more details.
|
||||
|
||||
[[pidfile]]*pidfile* _file_::
|
||||
*chronyd* always writes its process ID (PID) to a file, and checks this file on
|
||||
startup to see if another *chronyd* might already be running on the system. By
|
||||
default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
|
||||
allows the name to be changed, e.g.:
|
||||
Unless *chronyd* is started with the *-Q* option, it writes its process ID
|
||||
(PID) to a file, and checks this file on startup to see if another *chronyd*
|
||||
might already be running on the system. By default, the file used is
|
||||
_@DEFAULT_PID_FILE@_. The *pidfile* directive allows the name to be changed,
|
||||
e.g.:
|
||||
+
|
||||
----
|
||||
pidfile /run/chronyd.pid
|
||||
----
|
||||
|
||||
[[sched_priority]]*sched_priority* _priority_::
|
||||
On Linux, the *sched_priority* directive will select the SCHED_FIFO real-time
|
||||
scheduler at the specified priority (which must be between 0 and 100). On
|
||||
macOS, this option must have either a value of 0 (the default) to disable the
|
||||
thread time constraint policy or 1 for the policy to be enabled. Other systems
|
||||
do not support this option.
|
||||
On Linux, FreeBSD, NetBSD, and Solaris, the *sched_priority* directive will
|
||||
select the SCHED_FIFO real-time scheduler at the specified priority (which must
|
||||
be between 0 and 100). On macOS, this option must have either a value of 0 (the
|
||||
default) to disable the thread time constraint policy or 1 for the policy to be
|
||||
enabled.
|
||||
+
|
||||
On Linux, this directive uses the *sched_setscheduler()* system call to
|
||||
instruct the kernel to use the SCHED_FIFO first-in, first-out real-time
|
||||
scheduling policy for *chronyd* with the specified priority. This means that
|
||||
whenever *chronyd* is ready to run it will run, interrupting whatever else is
|
||||
running unless it is a higher priority real-time process. This should not
|
||||
impact performance as *chronyd* resource requirements are modest, but it should
|
||||
result in lower and more consistent latency since *chronyd* will not need to
|
||||
wait for the scheduler to get around to running it. You should not use this
|
||||
unless you really need it. The *sched_setscheduler(2)* man page has more
|
||||
details.
|
||||
On systems other than macOS, this directive uses the *pthread_setschedparam()*
|
||||
system call to instruct the kernel to use the SCHED_FIFO first-in, first-out
|
||||
real-time scheduling policy for *chronyd* with the specified priority. This
|
||||
means that whenever *chronyd* is ready to run it will run, interrupting
|
||||
whatever else is running unless it is a higher priority real-time process. This
|
||||
should not impact performance as *chronyd* resource requirements are modest,
|
||||
but it should result in lower and more consistent latency since *chronyd* will
|
||||
not need to wait for the scheduler to get around to running it. You should not
|
||||
use this unless you really need it. The *pthread_setschedparam(3)* man page has
|
||||
more details.
|
||||
+
|
||||
On macOS, this directive uses the *thread_policy_set()* kernel call to
|
||||
specify real-time scheduling. As noted for Linux, you should not use this
|
||||
directive unless you really need it.
|
||||
specify real-time scheduling. As noted above, you should not use this directive
|
||||
unless you really need it.
|
||||
|
||||
[[user]]*user* _user_::
|
||||
The *user* directive sets the name of the system user to which *chronyd* will
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
116
doc/faq.adoc
116
doc/faq.adoc
@@ -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,40 @@ 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
|
||||
----
|
||||
|
||||
For best stability, the CPU should be running at a constant frequency (i.e.
|
||||
disabled power saving and performance boosting). Energy-Efficient Ethernet
|
||||
(EEE) should be disabled in the network. The switches should be configured to
|
||||
prioritize NTP packets, especially if the network is expected to be heavily
|
||||
loaded.
|
||||
|
||||
If it is acceptable for NTP clients in the network to send requests at an
|
||||
excessive rate, a sub-second polling interval may be specified. A median filter
|
||||
can be enabled in order to update the clock at a reduced rate with more stable
|
||||
measurements. For example:
|
||||
|
||||
----
|
||||
server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
|
||||
hwtimestamp eth0 minpoll -6
|
||||
----
|
||||
|
||||
=== Does `chronyd` have an ntpdate mode?
|
||||
|
||||
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
|
||||
@@ -216,6 +244,33 @@ specified on the command line. For example:
|
||||
# chronyd -q 'pool pool.ntp.org iburst'
|
||||
----
|
||||
|
||||
=== Can `chronyd` be configured to control the clock like `ntpd`?
|
||||
|
||||
It is not possible to perfectly emulate `ntpd`, but there are some options that
|
||||
can configure `chronyd` to behave more like `ntpd`.
|
||||
|
||||
In the following example the `minsamples` directive slows down the response to
|
||||
changes in the frequency and offset of the clock. The `maxslewrate` and
|
||||
`corrtimeratio` directives reduce the maximum frequency error due to an offset
|
||||
correction and the `maxdrift` directive reduces the maximum assumed frequency
|
||||
error of the clock. The `makestep` directive enables a step threshold and the
|
||||
`maxchange` directive enables a panic threshold. The `maxclockerror` directive
|
||||
increases the minimum dispersion rate.
|
||||
|
||||
----
|
||||
minsamples 32
|
||||
maxslewrate 500
|
||||
corrtimeratio 100
|
||||
maxdrift 500
|
||||
makestep 0.128 -1
|
||||
maxchange 1000 1 1
|
||||
maxclockerror 15
|
||||
----
|
||||
|
||||
Note that increasing `minsamples` may cause the offsets in the `tracking` and
|
||||
`sourcestats` reports/logs to be significantly smaller than the actual offsets
|
||||
and be unsuitable for monitoring.
|
||||
|
||||
=== What happened to the `commandkey` and `generatecommandkey` directives?
|
||||
|
||||
They were removed in version 2.2. Authentication is no longer supported in the
|
||||
@@ -292,6 +347,49 @@ to
|
||||
makestep 1 -1
|
||||
----
|
||||
|
||||
=== Using a Windows NTP server?
|
||||
|
||||
A common issue with Windows NTP servers is that they report a very large root
|
||||
dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
|
||||
server for being too inaccurate. The `sources` command may show a valid
|
||||
measurement, but the server is not selected for synchronisation. You can check
|
||||
the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
|
||||
|
||||
The `maxdistance` value needs to be increased in _chrony.conf_ to enable
|
||||
synchronisation to such a server. For example:
|
||||
|
||||
----
|
||||
maxdistance 16.0
|
||||
----
|
||||
|
||||
=== Using a PPS reference clock?
|
||||
|
||||
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
|
||||
determine which second of UTC corresponds to each pulse. If it is another
|
||||
reference clock specified with the `lock` option in the `refclock` directive,
|
||||
the offset between the two reference clocks must be smaller than 0.2 seconds in
|
||||
order for the PPS reference clock to work. With NMEA reference clocks it is
|
||||
common to have a larger offset. It needs to be corrected with the `offset`
|
||||
option.
|
||||
|
||||
One approach to find out a good value of the `offset` option is to configure
|
||||
the reference clocks with the `noselect` option and compare them to an NTP
|
||||
server. For example, if the `sourcestats` command showed
|
||||
|
||||
----
|
||||
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
|
||||
==============================================================================
|
||||
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
|
||||
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
|
||||
foo.example.net 7 3 200 -2.991 16.141 -107us 492us
|
||||
----
|
||||
|
||||
the offset of the NMEA source would need to be increased by about 0.504
|
||||
seconds. It does not have to be very accurate. As long as the offset of the
|
||||
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
|
||||
able to determine the seconds corresponding to the pulses and allow the samples
|
||||
to be used for synchronisation.
|
||||
|
||||
== Issues with `chronyc`
|
||||
|
||||
=== I keep getting the error `506 Cannot talk to daemon`
|
||||
|
||||
@@ -22,6 +22,19 @@ The software is distributed as source code which has to be compiled. The source
|
||||
code is supplied in the form of a gzipped tar file, which unpacks to a
|
||||
subdirectory identifying the name and version of the program.
|
||||
|
||||
The following programs and libraries with their development files are needed to
|
||||
build `chrony`:
|
||||
|
||||
* C compiler (gcc or clang recommended)
|
||||
* GNU Make
|
||||
* Nettle, NSS, or LibTomCrypt (optional)
|
||||
* Editline (optional)
|
||||
* libcap (Linux only, optional)
|
||||
* libseccomp (Linux only, optional)
|
||||
* timepps.h header (optional)
|
||||
* Asciidoctor (for HTML documentation)
|
||||
* Bash (for testing)
|
||||
|
||||
After unpacking the source code, change directory into it, and type
|
||||
|
||||
----
|
||||
|
||||
@@ -95,7 +95,7 @@ driftfile /var/lib/chrony/drift
|
||||
# still running and bail out. If you want to change the path to the PID
|
||||
# file, uncomment this line and edit it. The default path is shown.
|
||||
|
||||
! pidfile /var/run/chronyd.pid
|
||||
! pidfile /var/run/chrony/chronyd.pid
|
||||
|
||||
# If the system timezone database is kept up to date and includes the
|
||||
# right/UTC timezone, chronyd can use it to determine the current
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# This is an example chrony keys file. It is used for NTP authentication with
|
||||
# symmetric keys. It should be readable only by root or the user to which
|
||||
# chronyd is configured to switch to after start.
|
||||
# This is an example chrony keys file. It enables authentication of NTP
|
||||
# packets with symmetric keys when its location is specified by the keyfile
|
||||
# directive in chrony.conf(5). It should be readable only by root and the
|
||||
# user under which chronyd is running.
|
||||
#
|
||||
# Don't use the example keys! It's recommended to generate random keys using
|
||||
# the chronyc keygen command.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
%global chrony_version @@VERSION@@
|
||||
%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
|
||||
%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
|
||||
%endif
|
||||
Summary: An NTP client/server
|
||||
Name: chrony
|
||||
Version: %(echo %{chrony_version} | sed 's/-.*//')
|
||||
Release: %{!?prerelease:1}%{?prerelease:0.1.%{prerelease}}
|
||||
Source: chrony-%{version}%{?prerelease:-%{prerelease}}.tar.gz
|
||||
License: GPLv2
|
||||
Group: Applications/Utilities
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n)
|
||||
|
||||
%description
|
||||
chrony is a client and server for the Network Time Protocol (NTP).
|
||||
This program keeps your computer's clock accurate. It was specially
|
||||
designed to support systems with intermittent Internet connections,
|
||||
but it also works well in permanently connected environments. It can
|
||||
also use hardware reference clocks, the system real-time clock, or
|
||||
manual input as time references.
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}-%{version}%{?prerelease:-%{prerelease}}
|
||||
|
||||
%build
|
||||
./configure \
|
||||
--prefix=%{_prefix} \
|
||||
--bindir=%{_bindir} \
|
||||
--sbindir=%{_sbindir} \
|
||||
--mandir=%{_mandir}
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make install DESTDIR=$RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%{_sbindir}/chronyd
|
||||
%{_bindir}/chronyc
|
||||
%{_mandir}/man1/chronyc.1.gz
|
||||
%{_mandir}/man5/chrony.conf.5.gz
|
||||
%{_mandir}/man8/chronyd.8.gz
|
||||
%doc README FAQ NEWS COPYING
|
||||
%doc examples/chrony.conf.example*
|
||||
%doc examples/chrony.keys.example
|
||||
|
||||
@@ -7,7 +7,7 @@ ConditionCapability=CAP_SYS_TIME
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/var/run/chronyd.pid
|
||||
PIDFile=/run/chrony/chronyd.pid
|
||||
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||
ExecStart=/usr/sbin/chronyd $OPTIONS
|
||||
PrivateTmp=yes
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "sysincl.h"
|
||||
#include "hash.h"
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "md5.c"
|
||||
|
||||
@@ -49,18 +50,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
|
||||
const unsigned char *in2, unsigned int in2_len,
|
||||
unsigned char *out, unsigned int out_len)
|
||||
{
|
||||
if (out_len < 16)
|
||||
return 0;
|
||||
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, in1, in1_len);
|
||||
if (in2)
|
||||
MD5Update(&ctx, in2, in2_len);
|
||||
MD5Final(&ctx);
|
||||
|
||||
memcpy(out, ctx.digest, 16);
|
||||
out_len = MIN(out_len, 16);
|
||||
|
||||
return 16;
|
||||
memcpy(out, ctx.digest, out_len);
|
||||
|
||||
return out_len;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2012
|
||||
* Copyright (C) Miroslav Lichvar 2012, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
|
||||
struct hash {
|
||||
const char *name;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
42
hwclock.c
42
hwclock.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016-2017
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -36,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 */
|
||||
|
||||
@@ -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
4
keys.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
26
logging.c
26
logging.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -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);
|
||||
@@ -169,6 +171,7 @@ void LOG_Message(LOG_Severity severity,
|
||||
system_log = 0;
|
||||
log_message(1, severity, buf);
|
||||
}
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
@@ -182,13 +185,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
20
main.c
20
main.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2012-2017
|
||||
* Copyright (C) Miroslav Lichvar 2012-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -281,13 +281,9 @@ write_pidfile(void)
|
||||
if (!pidfile[0])
|
||||
return;
|
||||
|
||||
out = fopen(pidfile, "w");
|
||||
if (!out) {
|
||||
LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno));
|
||||
} else {
|
||||
fprintf(out, "%d\n", (int)getpid());
|
||||
fclose(out);
|
||||
}
|
||||
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
|
||||
fprintf(out, "%d\n", (int)getpid());
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -530,9 +526,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 +536,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 +582,7 @@ int main
|
||||
/* From now on, it is safe to do finalisation on exit */
|
||||
initialised = 1;
|
||||
|
||||
UTI_SetQuitSignalsHandler(signal_cleanup);
|
||||
UTI_SetQuitSignalsHandler(signal_cleanup, 1);
|
||||
|
||||
CAM_OpenUnixSocket();
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@ cd RELEASES/$subdir || exit 1
|
||||
|
||||
echo $version > version.txt
|
||||
|
||||
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
|
||||
|
||||
./configure && make -C doc man txt || exit 1
|
||||
|
||||
iconv -f utf-8 -t ascii//TRANSLIT < doc/installation.txt > INSTALL
|
||||
|
||||
2
memory.h
2
memory.h
@@ -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);
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#include "nameserv.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
15
ntp.h
15
ntp.h
@@ -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 */
|
||||
|
||||
350
ntp_core.c
350
ntp_core.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
68
ntp_io.c
68
ntp_io.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Timo Teras 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2016
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -105,6 +105,9 @@ static int separate_client_sockets;
|
||||
disabled */
|
||||
static int permanent_server_sockets;
|
||||
|
||||
/* Flag indicating the server IPv4 socket is bound to an address */
|
||||
static int bound_server_sock_fd4;
|
||||
|
||||
/* Flag indicating that we have been initialised */
|
||||
static int initialised=0;
|
||||
|
||||
@@ -142,6 +145,10 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
/* Close on exec */
|
||||
UTI_FdSetCloexec(sock_fd);
|
||||
|
||||
/* Enable non-blocking mode on server sockets */
|
||||
if (!client_only && fcntl(sock_fd, F_SETFL, O_NONBLOCK))
|
||||
DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
|
||||
|
||||
/* Prepare local address */
|
||||
memset(&my_addr, 0, sizeof (my_addr));
|
||||
my_addr_len = 0;
|
||||
@@ -164,6 +171,9 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
my_addr.in4.sin_port = htons(port_number);
|
||||
my_addr_len = sizeof (my_addr.in4);
|
||||
|
||||
if (!client_only)
|
||||
bound_server_sock_fd4 = my_addr.in4.sin_addr.s_addr != htonl(INADDR_ANY);
|
||||
|
||||
break;
|
||||
#ifdef FEAT_IPV6
|
||||
case AF_INET6:
|
||||
@@ -227,11 +237,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 +580,23 @@ NIO_IsServerSocket(int sock_fd)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
|
||||
{
|
||||
int sock_fd, r;
|
||||
|
||||
sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
|
||||
if (sock_fd == INVALID_SOCK_FD)
|
||||
return 0;
|
||||
|
||||
r = connect_socket(sock_fd, remote_addr);
|
||||
close_socket(sock_fd);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
process_message(struct msghdr *hdr, int length, int sock_fd)
|
||||
{
|
||||
@@ -621,6 +648,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 +698,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));
|
||||
@@ -806,6 +841,25 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
|
||||
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
|
||||
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
|
||||
if (local_addr->if_index != INVALID_IF_INDEX)
|
||||
ipi->ipi_ifindex = local_addr->if_index;
|
||||
}
|
||||
#elif defined(IP_SENDSRCADDR)
|
||||
/* Specify the IPv4 source address only if the socket is not bound */
|
||||
if (local_addr->ip_addr.family == IPADDR_INET4 &&
|
||||
local_addr->sock_fd == server_sock_fd4 && !bound_server_sock_fd4) {
|
||||
struct in_addr *addr;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
memset(cmsg, 0, CMSG_SPACE(sizeof (struct in_addr)));
|
||||
cmsglen += CMSG_SPACE(sizeof (struct in_addr));
|
||||
|
||||
cmsg->cmsg_level = IPPROTO_IP;
|
||||
cmsg->cmsg_type = IP_SENDSRCADDR;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_addr));
|
||||
|
||||
addr = (struct in_addr *)CMSG_DATA(cmsg);
|
||||
addr->s_addr = htonl(local_addr->ip_addr.addr.in4);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -824,6 +878,8 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
|
||||
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
|
||||
sizeof(ipi->ipi6_addr.s6_addr));
|
||||
if (local_addr->if_index != INVALID_IF_INDEX)
|
||||
ipi->ipi6_ifindex = local_addr->if_index;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
3
ntp_io.h
3
ntp_io.h
@@ -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);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
||||
* Copyright (C) Miroslav Lichvar 2016-2019
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -123,7 +123,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
||||
struct ethtool_ts_info ts_info;
|
||||
struct hwtstamp_config ts_config;
|
||||
struct ifreq req;
|
||||
int sock_fd, if_index, phc_fd, req_hwts_flags;
|
||||
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
|
||||
unsigned int i;
|
||||
struct Interface *iface;
|
||||
|
||||
@@ -171,40 +171,57 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ts_config.flags = 0;
|
||||
ts_config.tx_type = HWTSTAMP_TX_ON;
|
||||
if (ts_info.phc_index < 0) {
|
||||
DEBUG_LOG("PHC missing on %s", req.ifr_name);
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (conf_iface->rxfilter) {
|
||||
case CNF_HWTS_RXFILTER_ANY:
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
|
||||
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_NTP_ALL))
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
||||
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
||||
else
|
||||
#endif
|
||||
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
else
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
|
||||
rx_filter = HWTSTAMP_FILTER_NONE;
|
||||
break;
|
||||
case CNF_HWTS_RXFILTER_NONE:
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
|
||||
rx_filter = HWTSTAMP_FILTER_NONE;
|
||||
break;
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
|
||||
case CNF_HWTS_RXFILTER_NTP:
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
||||
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
break;
|
||||
}
|
||||
|
||||
ts_config.flags = 0;
|
||||
ts_config.tx_type = HWTSTAMP_TX_ON;
|
||||
ts_config.rx_filter = rx_filter;
|
||||
req.ifr_data = (char *)&ts_config;
|
||||
|
||||
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
|
||||
/* Check the current timestamping configuration in case this interface
|
||||
allows only reading of the configuration and it was already configured
|
||||
as requested */
|
||||
req.ifr_data = (char *)&ts_config;
|
||||
#ifdef SIOCGHWTSTAMP
|
||||
if (ioctl(sock_fd, SIOCGHWTSTAMP, &req) ||
|
||||
ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter)
|
||||
#endif
|
||||
{
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
close(sock_fd);
|
||||
@@ -230,7 +247,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 +333,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;
|
||||
}
|
||||
@@ -577,7 +595,11 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
||||
if (rx_ntp_length && iface->link_speed) {
|
||||
if (!l2_length)
|
||||
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
|
||||
iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
|
||||
iface->l2_udp6_ntp_start) + rx_ntp_length;
|
||||
|
||||
/* Include the frame check sequence (FCS) */
|
||||
l2_length += 4;
|
||||
|
||||
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
|
||||
|
||||
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
|
||||
@@ -784,7 +806,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);
|
||||
|
||||
@@ -834,7 +856,12 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
|
||||
/* Add control message that will enable TX timestamping for this message.
|
||||
Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
|
||||
control messages. */
|
||||
cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(msg);
|
||||
if (!cmsg || cmsglen + CMSG_SPACE(sizeof (ts_tx_flags)) > msg->msg_controllen)
|
||||
return cmsglen;
|
||||
|
||||
cmsg = (struct cmsghdr *)((char *)cmsg + cmsglen);
|
||||
memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
|
||||
cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
496
refclock.c
496
refclock.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2017
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "sources.h"
|
||||
#include "logging.h"
|
||||
#include "regress.h"
|
||||
#include "samplefilt.h"
|
||||
#include "sched.h"
|
||||
|
||||
/* list of refclock drivers */
|
||||
@@ -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);
|
||||
@@ -332,25 +325,57 @@ RCL_GetDriverParameter(RCL_Instance instance)
|
||||
return instance->driver_parameter;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_next_driver_option(RCL_Instance instance, char *option)
|
||||
{
|
||||
if (option == NULL)
|
||||
option = instance->driver_parameter;
|
||||
|
||||
option += strlen(option) + 1;
|
||||
|
||||
if (option >= instance->driver_parameter + instance->driver_parameter_length)
|
||||
return NULL;
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
void
|
||||
RCL_CheckDriverOptions(RCL_Instance instance, const char **options)
|
||||
{
|
||||
char *option;
|
||||
int i, len;
|
||||
|
||||
for (option = get_next_driver_option(instance, NULL);
|
||||
option;
|
||||
option = get_next_driver_option(instance, option)) {
|
||||
for (i = 0; options && options[i]; i++) {
|
||||
len = strlen(options[i]);
|
||||
if (!strncmp(options[i], option, len) &&
|
||||
(option[len] == '=' || option[len] == '\0'))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!options || !options[i])
|
||||
LOG_FATAL("Invalid refclock driver option %s", option);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
RCL_GetDriverOption(RCL_Instance instance, char *name)
|
||||
{
|
||||
char *s, *e;
|
||||
int n;
|
||||
char *option;
|
||||
int len;
|
||||
|
||||
s = instance->driver_parameter;
|
||||
e = s + instance->driver_parameter_length;
|
||||
n = strlen(name);
|
||||
len = strlen(name);
|
||||
|
||||
while (1) {
|
||||
s += strlen(s) + 1;
|
||||
if (s >= e)
|
||||
break;
|
||||
if (!strncmp(name, s, n)) {
|
||||
if (s[n] == '=')
|
||||
return s + n + 1;
|
||||
if (s[n] == '\0')
|
||||
return s + n;
|
||||
for (option = get_next_driver_option(instance, NULL);
|
||||
option;
|
||||
option = get_next_driver_option(instance, option)) {
|
||||
if (!strncmp(name, option, len)) {
|
||||
if (option[len] == '=')
|
||||
return option + len + 1;
|
||||
if (option[len] == '\0')
|
||||
return option + len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,6 +404,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 +460,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 +539,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 +559,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 +597,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 +605,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 +636,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 +686,7 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
|
||||
static void
|
||||
poll_timeout(void *arg)
|
||||
{
|
||||
NTP_Sample sample;
|
||||
int poll;
|
||||
|
||||
RCL_Instance inst = (RCL_Instance)arg;
|
||||
@@ -651,26 +700,14 @@ poll_timeout(void *arg)
|
||||
}
|
||||
|
||||
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
|
||||
double offset, dispersion;
|
||||
struct timespec sample_time;
|
||||
int sample_ok, stratum;
|
||||
|
||||
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
|
||||
inst->driver_polled = 0;
|
||||
|
||||
if (sample_ok) {
|
||||
if (inst->pps_active && inst->lock_ref == -1)
|
||||
/* Handle special case when PPS is used with local stratum */
|
||||
stratum = pps_stratum(inst, &sample_time);
|
||||
else
|
||||
stratum = 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 +724,9 @@ slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
|
||||
for (i = 0; i < ARR_GetSize(refclocks); i++) {
|
||||
if (change_type == LCL_ChangeUnknownStep)
|
||||
filter_reset(&get_refclock(i)->filter);
|
||||
SPF_DropSamples(get_refclock(i)->filter);
|
||||
else
|
||||
filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset);
|
||||
SPF_SlewSamples(get_refclock(i)->filter, cooked, dfreq, doffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,7 +736,7 @@ add_dispersion(double dispersion, void *anything)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARR_GetSize(refclocks); i++)
|
||||
filter_add_dispersion(&get_refclock(i)->filter, dispersion);
|
||||
SPF_AddDispersion(get_refclock(i)->filter, dispersion);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -731,320 +768,3 @@ log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, in
|
||||
dispersion);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
filter_init(struct MedianFilter *filter, int length, double max_dispersion)
|
||||
{
|
||||
if (length < 1)
|
||||
length = 1;
|
||||
|
||||
filter->length = length;
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
filter->last = -1;
|
||||
/* set first estimate to system precision */
|
||||
filter->avg_var_n = 0;
|
||||
filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
|
||||
filter->max_var = max_dispersion * max_dispersion;
|
||||
filter->samples = MallocArray(struct FilterSample, filter->length);
|
||||
filter->selected = MallocArray(int, filter->length);
|
||||
filter->x_data = MallocArray(double, filter->length);
|
||||
filter->y_data = MallocArray(double, filter->length);
|
||||
filter->w_data = MallocArray(double, filter->length);
|
||||
}
|
||||
|
||||
static void
|
||||
filter_fini(struct MedianFilter *filter)
|
||||
{
|
||||
Free(filter->samples);
|
||||
Free(filter->selected);
|
||||
Free(filter->x_data);
|
||||
Free(filter->y_data);
|
||||
Free(filter->w_data);
|
||||
}
|
||||
|
||||
static void
|
||||
filter_reset(struct MedianFilter *filter)
|
||||
{
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
}
|
||||
|
||||
static double
|
||||
filter_get_avg_sample_dispersion(struct MedianFilter *filter)
|
||||
{
|
||||
return sqrt(filter->avg_var);
|
||||
}
|
||||
|
||||
static void
|
||||
filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
|
||||
{
|
||||
filter->index++;
|
||||
filter->index %= filter->length;
|
||||
filter->last = filter->index;
|
||||
if (filter->used < filter->length)
|
||||
filter->used++;
|
||||
|
||||
filter->samples[filter->index].sample_time = *sample_time;
|
||||
filter->samples[filter->index].offset = offset;
|
||||
filter->samples[filter->index].dispersion = dispersion;
|
||||
|
||||
DEBUG_LOG("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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
|
||||
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
|
||||
extern void *RCL_GetDriverData(RCL_Instance instance);
|
||||
extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
||||
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
||||
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
||||
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
|
||||
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
|
||||
|
||||
@@ -56,10 +56,13 @@ static void read_ext_pulse(int sockfd, int event, void *anything);
|
||||
|
||||
static int phc_initialise(RCL_Instance instance)
|
||||
{
|
||||
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
|
||||
struct phc_instance *phc;
|
||||
int phc_fd, rising_edge;
|
||||
char *path, *s;
|
||||
|
||||
RCL_CheckDriverOptions(instance, options);
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
|
||||
phc_fd = SYS_Linux_OpenPHC(path, 0);
|
||||
@@ -80,7 +83,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))
|
||||
|
||||
@@ -48,35 +48,38 @@ struct pps_instance {
|
||||
};
|
||||
|
||||
static int pps_initialise(RCL_Instance instance) {
|
||||
const char *options[] = {"clear", NULL};
|
||||
pps_handle_t handle;
|
||||
pps_params_t params;
|
||||
struct pps_instance *pps;
|
||||
int fd, edge_clear, mode;
|
||||
char *path;
|
||||
|
||||
RCL_CheckDriverOptions(instance, options);
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0;
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd < 0) {
|
||||
LOG_FATAL("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, ¶ms) < 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 +100,7 @@ static int pps_initialise(RCL_Instance instance) {
|
||||
}
|
||||
|
||||
if (time_pps_setparams(handle, ¶ms) < 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 +150,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,23 +59,26 @@ struct shmTime {
|
||||
};
|
||||
|
||||
static int shm_initialise(RCL_Instance instance) {
|
||||
const char *options[] = {"perm", NULL};
|
||||
int id, param, perm;
|
||||
char *s;
|
||||
struct shmTime *shm;
|
||||
|
||||
RCL_CheckDriverOptions(instance, options);
|
||||
|
||||
param = atoi(RCL_GetDriverParameter(instance));
|
||||
s = RCL_GetDriverOption(instance, "perm");
|
||||
perm = s ? strtol(s, NULL, 8) & 0777 : 0600;
|
||||
|
||||
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
|
||||
if (id == -1) {
|
||||
LOG_FATAL("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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -102,11 +101,13 @@ static int sock_initialise(RCL_Instance instance)
|
||||
int sockfd;
|
||||
char *path;
|
||||
|
||||
RCL_CheckDriverOptions(instance, NULL);
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
|
||||
s.sun_family = AF_UNIX;
|
||||
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
|
||||
LOG_FATAL("path %s is too long", path);
|
||||
LOG_FATAL("Path %s too long", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ static int sock_initialise(RCL_Instance instance)
|
||||
|
||||
unlink(path);
|
||||
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
|
||||
LOG_FATAL("bind() failed");
|
||||
LOG_FATAL("bind(%s) failed : %s", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
220
reference.c
220
reference.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2017
|
||||
* Copyright (C) Miroslav Lichvar 2009-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -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
|
||||
|
||||
14
rtc_linux.c
14
rtc_linux.c
@@ -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
453
samplefilt.c
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2014, 2016, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Routines implementing a median sample filter.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "regress.h"
|
||||
#include "samplefilt.h"
|
||||
#include "util.h"
|
||||
|
||||
#define MIN_SAMPLES 1
|
||||
#define MAX_SAMPLES 256
|
||||
|
||||
struct SPF_Instance_Record {
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int index;
|
||||
int used;
|
||||
int last;
|
||||
int avg_var_n;
|
||||
double avg_var;
|
||||
double max_var;
|
||||
double combine_ratio;
|
||||
NTP_Sample *samples;
|
||||
int *selected;
|
||||
double *x_data;
|
||||
double *y_data;
|
||||
double *w_data;
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
SPF_Instance
|
||||
SPF_CreateInstance(int min_samples, int max_samples, double max_dispersion, double combine_ratio)
|
||||
{
|
||||
SPF_Instance filter;
|
||||
|
||||
filter = MallocNew(struct SPF_Instance_Record);
|
||||
|
||||
min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
|
||||
max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
|
||||
max_samples = MAX(min_samples, max_samples);
|
||||
combine_ratio = CLAMP(0.0, combine_ratio, 1.0);
|
||||
|
||||
filter->min_samples = min_samples;
|
||||
filter->max_samples = max_samples;
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
filter->last = -1;
|
||||
/* Set the first estimate to the system precision */
|
||||
filter->avg_var_n = 0;
|
||||
filter->avg_var = SQUARE(LCL_GetSysPrecisionAsQuantum());
|
||||
filter->max_var = SQUARE(max_dispersion);
|
||||
filter->combine_ratio = combine_ratio;
|
||||
filter->samples = MallocArray(NTP_Sample, filter->max_samples);
|
||||
filter->selected = MallocArray(int, filter->max_samples);
|
||||
filter->x_data = MallocArray(double, filter->max_samples);
|
||||
filter->y_data = MallocArray(double, filter->max_samples);
|
||||
filter->w_data = MallocArray(double, filter->max_samples);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_DestroyInstance(SPF_Instance filter)
|
||||
{
|
||||
Free(filter->samples);
|
||||
Free(filter->selected);
|
||||
Free(filter->x_data);
|
||||
Free(filter->y_data);
|
||||
Free(filter->w_data);
|
||||
Free(filter);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Check that samples times are strictly increasing */
|
||||
|
||||
static int
|
||||
check_sample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
if (filter->used <= 0)
|
||||
return 1;
|
||||
|
||||
if (UTI_CompareTimespecs(&filter->samples[filter->last].time, &sample->time) >= 0) {
|
||||
DEBUG_LOG("filter non-increasing sample time %s", UTI_TimespecToString(&sample->time));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
if (!check_sample(filter, sample))
|
||||
return 0;
|
||||
|
||||
filter->index++;
|
||||
filter->index %= filter->max_samples;
|
||||
filter->last = filter->index;
|
||||
if (filter->used < filter->max_samples)
|
||||
filter->used++;
|
||||
|
||||
filter->samples[filter->index] = *sample;
|
||||
|
||||
DEBUG_LOG("filter sample %d t=%s offset=%.9f peer_disp=%.9f",
|
||||
filter->index, UTI_TimespecToString(&sample->time),
|
||||
sample->offset, sample->peer_dispersion);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
if (filter->last < 0)
|
||||
return 0;
|
||||
|
||||
*sample = filter->samples[filter->last];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_GetNumberOfSamples(SPF_Instance filter)
|
||||
{
|
||||
return filter->used;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
||||
{
|
||||
return sqrt(filter->avg_var);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_DropSamples(SPF_Instance filter)
|
||||
{
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static const NTP_Sample *tmp_sort_samples;
|
||||
|
||||
static int
|
||||
compare_samples(const void *a, const void *b)
|
||||
{
|
||||
const NTP_Sample *s1, *s2;
|
||||
|
||||
s1 = &tmp_sort_samples[*(int *)a];
|
||||
s2 = &tmp_sort_samples[*(int *)b];
|
||||
|
||||
if (s1->offset < s2->offset)
|
||||
return -1;
|
||||
else if (s1->offset > s2->offset)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
select_samples(SPF_Instance filter)
|
||||
{
|
||||
int i, j, k, o, from, to, *selected;
|
||||
double min_dispersion;
|
||||
|
||||
if (filter->used < filter->min_samples)
|
||||
return 0;
|
||||
|
||||
selected = filter->selected;
|
||||
|
||||
/* With 4 or more samples, select those that have peer dispersion smaller
|
||||
than 1.5x of the minimum dispersion */
|
||||
if (filter->used > 4) {
|
||||
for (i = 1, min_dispersion = filter->samples[0].peer_dispersion; i < filter->used; i++) {
|
||||
if (min_dispersion > filter->samples[i].peer_dispersion)
|
||||
min_dispersion = filter->samples[i].peer_dispersion;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < filter->used; i++) {
|
||||
if (filter->samples[i].peer_dispersion <= 1.5 * min_dispersion)
|
||||
selected[j++] = i;
|
||||
}
|
||||
} else {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
if (j < 4) {
|
||||
/* Select all samples */
|
||||
|
||||
for (j = 0; j < filter->used; j++)
|
||||
selected[j] = j;
|
||||
}
|
||||
|
||||
/* And sort their indices by offset */
|
||||
tmp_sort_samples = filter->samples;
|
||||
qsort(selected, j, sizeof (int), compare_samples);
|
||||
|
||||
/* Select samples closest to the median */
|
||||
if (j > 2) {
|
||||
from = j * (1.0 - filter->combine_ratio) / 2.0;
|
||||
from = CLAMP(1, from, (j - 1) / 2);
|
||||
} else {
|
||||
from = 0;
|
||||
}
|
||||
|
||||
to = j - from;
|
||||
|
||||
/* Mark unused samples and sort the rest by their time */
|
||||
|
||||
o = filter->used - filter->index - 1;
|
||||
|
||||
for (i = 0; i < from; i++)
|
||||
selected[i] = -1;
|
||||
for (; i < to; i++)
|
||||
selected[i] = (selected[i] + o) % filter->used;
|
||||
for (; i < filter->used; i++)
|
||||
selected[i] = -1;
|
||||
|
||||
for (i = from; i < to; i++) {
|
||||
j = selected[i];
|
||||
selected[i] = -1;
|
||||
while (j != -1 && selected[j] != j) {
|
||||
k = selected[j];
|
||||
selected[j] = j;
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = j = 0, k = -1; i < filter->used; i++) {
|
||||
if (selected[i] != -1)
|
||||
selected[j++] = (selected[i] + filter->used - o) % filter->used;
|
||||
}
|
||||
|
||||
assert(j > 0 && j <= filter->max_samples);
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
|
||||
{
|
||||
double mean_peer_dispersion, mean_root_dispersion, mean_peer_delay, mean_root_delay;
|
||||
double mean_x, mean_y, disp, var, prev_avg_var;
|
||||
NTP_Sample *sample, *last_sample;
|
||||
int i, dof;
|
||||
|
||||
last_sample = &filter->samples[filter->selected[n - 1]];
|
||||
|
||||
/* Prepare data */
|
||||
for (i = 0; i < n; i++) {
|
||||
sample = &filter->samples[filter->selected[i]];
|
||||
|
||||
filter->x_data[i] = UTI_DiffTimespecsToDouble(&sample->time, &last_sample->time);
|
||||
filter->y_data[i] = sample->offset;
|
||||
filter->w_data[i] = sample->peer_dispersion;
|
||||
}
|
||||
|
||||
/* Calculate mean offset and interval since the last sample */
|
||||
for (i = 0, mean_x = mean_y = 0.0; i < n; i++) {
|
||||
mean_x += filter->x_data[i];
|
||||
mean_y += filter->y_data[i];
|
||||
}
|
||||
mean_x /= n;
|
||||
mean_y /= n;
|
||||
|
||||
if (n >= 4) {
|
||||
double b0, b1, s2, sb0, sb1;
|
||||
|
||||
/* Set y axis to the mean sample time */
|
||||
for (i = 0; i < n; i++)
|
||||
filter->x_data[i] -= mean_x;
|
||||
|
||||
/* Make a linear fit and use the estimated standard deviation of the
|
||||
intercept as dispersion */
|
||||
RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
|
||||
&b0, &b1, &s2, &sb0, &sb1);
|
||||
var = s2;
|
||||
disp = sb0;
|
||||
dof = n - 2;
|
||||
} else if (n >= 2) {
|
||||
for (i = 0, disp = 0.0; i < n; i++)
|
||||
disp += (filter->y_data[i] - mean_y) * (filter->y_data[i] - mean_y);
|
||||
var = disp / (n - 1);
|
||||
disp = sqrt(var);
|
||||
dof = n - 1;
|
||||
} else {
|
||||
var = filter->avg_var;
|
||||
disp = sqrt(var);
|
||||
dof = 1;
|
||||
}
|
||||
|
||||
/* Avoid working with zero dispersion */
|
||||
if (var < 1e-20) {
|
||||
var = 1e-20;
|
||||
disp = sqrt(var);
|
||||
}
|
||||
|
||||
/* Drop the sample if the variance is larger than the maximum */
|
||||
if (filter->max_var > 0.0 && var > filter->max_var) {
|
||||
DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
|
||||
sqrt(var), sqrt(filter->max_var));
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev_avg_var = filter->avg_var;
|
||||
|
||||
/* Update the exponential moving average of the variance */
|
||||
if (filter->avg_var_n > 50) {
|
||||
filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
|
||||
} else {
|
||||
filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
|
||||
(dof + filter->avg_var_n);
|
||||
if (filter->avg_var_n == 0)
|
||||
prev_avg_var = filter->avg_var;
|
||||
filter->avg_var_n += dof;
|
||||
}
|
||||
|
||||
/* Use the long-term average of variance instead of the estimated value
|
||||
unless it is significantly smaller in order to reduce the noise in
|
||||
sourcestats weights */
|
||||
if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
|
||||
disp = sqrt(filter->avg_var) * disp / sqrt(var);
|
||||
|
||||
mean_peer_dispersion = mean_root_dispersion = mean_peer_delay = mean_root_delay = 0.0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
sample = &filter->samples[filter->selected[i]];
|
||||
|
||||
mean_peer_dispersion += sample->peer_dispersion;
|
||||
mean_root_dispersion += sample->root_dispersion;
|
||||
mean_peer_delay += sample->peer_delay;
|
||||
mean_root_delay += sample->root_delay;
|
||||
}
|
||||
|
||||
mean_peer_dispersion /= n;
|
||||
mean_root_dispersion /= n;
|
||||
mean_peer_delay /= n;
|
||||
mean_root_delay /= n;
|
||||
|
||||
UTI_AddDoubleToTimespec(&last_sample->time, mean_x, &result->time);
|
||||
result->offset = mean_y;
|
||||
result->peer_dispersion = MAX(disp, mean_peer_dispersion);
|
||||
result->root_dispersion = MAX(disp, mean_root_dispersion);
|
||||
result->peer_delay = mean_peer_delay;
|
||||
result->root_delay = mean_root_delay;
|
||||
result->stratum = last_sample->stratum;
|
||||
result->leap = last_sample->leap;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = select_samples(filter);
|
||||
|
||||
if (n < 1)
|
||||
return 0;
|
||||
|
||||
if (!combine_selected_samples(filter, n, sample))
|
||||
return 0;
|
||||
|
||||
SPF_DropSamples(filter);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double doffset)
|
||||
{
|
||||
int i, first, last;
|
||||
double delta_time;
|
||||
|
||||
if (filter->last < 0)
|
||||
return;
|
||||
|
||||
/* Always slew the last sample as it may be returned even if no new
|
||||
samples were accumulated */
|
||||
if (filter->used > 0) {
|
||||
first = 0;
|
||||
last = filter->used - 1;
|
||||
} else {
|
||||
first = last = filter->last;
|
||||
}
|
||||
|
||||
for (i = first; i <= last; i++) {
|
||||
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
|
||||
&delta_time, dfreq, doffset);
|
||||
filter->samples[i].offset -= delta_time;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_AddDispersion(SPF_Instance filter, double dispersion)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < filter->used; i++) {
|
||||
filter->samples[i].peer_dispersion += dispersion;
|
||||
filter->samples[i].root_dispersion += dispersion;
|
||||
}
|
||||
}
|
||||
49
samplefilt.h
Normal file
49
samplefilt.h
Normal 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
|
||||
9
sched.c
9
sched.c
@@ -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--;
|
||||
}
|
||||
}
|
||||
|
||||
2
smooth.c
2
smooth.c
@@ -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
|
||||
|
||||
98
sources.c
98
sources.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -54,6 +54,7 @@ static int initialised = 0;
|
||||
/* ================================================== */
|
||||
/* Structure used to hold info for selecting between sources */
|
||||
struct SelectInfo {
|
||||
NTP_Leap leap;
|
||||
int stratum;
|
||||
int select_ok;
|
||||
double std_dev;
|
||||
@@ -91,7 +92,6 @@ typedef enum {
|
||||
source */
|
||||
struct SRC_Instance_Record {
|
||||
SST_Stats stats;
|
||||
NTP_Leap leap_status; /* Leap status */
|
||||
int index; /* Index back into the array of source */
|
||||
uint32_t ref_id; /* The reference ID of this source
|
||||
(i.e. from its IP address, NOT the
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
30
sources.h
30
sources.h
@@ -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);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014, 2016-2017
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
8
stubs.c
8
stubs.c
@@ -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;
|
||||
}
|
||||
|
||||
13
sys.c
13
sys.c
@@ -35,10 +35,13 @@
|
||||
|
||||
#if defined(LINUX)
|
||||
#include "sys_linux.h"
|
||||
#include "sys_posix.h"
|
||||
#elif defined(SOLARIS)
|
||||
#include "sys_solaris.h"
|
||||
#include "sys_posix.h"
|
||||
#elif defined(NETBSD) || defined(FREEBSD)
|
||||
#include "sys_netbsd.h"
|
||||
#include "sys_posix.h"
|
||||
#elif defined(MACOSX)
|
||||
#include "sys_macosx.h"
|
||||
#endif
|
||||
@@ -124,10 +127,10 @@ void SYS_EnableSystemCallFilter(int level)
|
||||
|
||||
void SYS_SetScheduler(int SchedPriority)
|
||||
{
|
||||
#if defined(LINUX) && defined(HAVE_SCHED_SETSCHEDULER)
|
||||
SYS_Linux_SetScheduler(SchedPriority);
|
||||
#elif defined(MACOSX)
|
||||
#if defined(MACOSX)
|
||||
SYS_MacOSX_SetScheduler(SchedPriority);
|
||||
#elif defined(HAVE_PTHREAD_SETSCHEDPARAM)
|
||||
SYS_Posix_SetScheduler(SchedPriority);
|
||||
#else
|
||||
LOG_FATAL("scheduler priority setting not supported");
|
||||
#endif
|
||||
@@ -137,8 +140,8 @@ void SYS_SetScheduler(int SchedPriority)
|
||||
|
||||
void SYS_LockMemory(void)
|
||||
{
|
||||
#if defined(LINUX) && defined(HAVE_MLOCKALL)
|
||||
SYS_Linux_MemLockAll(1);
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
SYS_Posix_MemLockAll();
|
||||
#else
|
||||
LOG_FATAL("memory locking not supported");
|
||||
#endif
|
||||
|
||||
242
sys_linux.c
242
sys_linux.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2017
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -33,20 +33,6 @@
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#if defined(HAVE_SCHED_SETSCHEDULER)
|
||||
# include <sched.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
# include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_PRIVDROP
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/capability.h>
|
||||
#endif
|
||||
|
||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||
#include <linux/ptp_clock.h>
|
||||
#endif
|
||||
@@ -66,6 +52,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 +300,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 +313,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 +331,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 +382,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");
|
||||
@@ -487,25 +484,27 @@ SYS_Linux_EnableSystemCallFilter(int level)
|
||||
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid),
|
||||
SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn),
|
||||
SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn),
|
||||
SCMP_SYS(wait4),
|
||||
SCMP_SYS(wait4), SCMP_SYS(waitpid),
|
||||
/* Memory */
|
||||
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2),
|
||||
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
|
||||
/* Filesystem */
|
||||
SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32),
|
||||
SCMP_SYS(_llseek), SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown),
|
||||
SCMP_SYS(chown32), SCMP_SYS(faccessat), SCMP_SYS(fchmodat), SCMP_SYS(fchownat),
|
||||
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64),
|
||||
SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
|
||||
SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
|
||||
SCMP_SYS(lseek), SCMP_SYS(newfstatat), SCMP_SYS(rename), SCMP_SYS(renameat),
|
||||
SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64),
|
||||
SCMP_SYS(unlink), SCMP_SYS(unlinkat),
|
||||
/* Socket */
|
||||
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(getsockopt),
|
||||
SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
|
||||
SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
|
||||
SCMP_SYS(recv), SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
|
||||
SCMP_SYS(send), SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
|
||||
/* TODO: check socketcall arguments */
|
||||
SCMP_SYS(socketcall),
|
||||
/* General I/O */
|
||||
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe),
|
||||
SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
|
||||
SCMP_SYS(set_robust_list), SCMP_SYS(write),
|
||||
SCMP_SYS(pipe2), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(pselect6), SCMP_SYS(read),
|
||||
SCMP_SYS(futex), SCMP_SYS(select), SCMP_SYS(set_robust_list), SCMP_SYS(write),
|
||||
/* Miscellaneous */
|
||||
SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname),
|
||||
};
|
||||
@@ -529,7 +528,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,
|
||||
@@ -538,6 +537,9 @@ SYS_Linux_EnableSystemCallFilter(int level)
|
||||
#ifdef PTP_PIN_SETFUNC
|
||||
PTP_PIN_SETFUNC,
|
||||
#endif
|
||||
#ifdef PTP_SYS_OFFSET_EXTENDED
|
||||
PTP_SYS_OFFSET_EXTENDED,
|
||||
#endif
|
||||
#ifdef PTP_SYS_OFFSET_PRECISE
|
||||
PTP_SYS_OFFSET_PRECISE,
|
||||
#endif
|
||||
@@ -621,63 +623,6 @@ add_failed:
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(HAVE_SCHED_SETSCHEDULER)
|
||||
/* Install SCHED_FIFO real-time scheduler with specified priority */
|
||||
void SYS_Linux_SetScheduler(int SchedPriority)
|
||||
{
|
||||
int pmax, pmin;
|
||||
struct sched_param sched;
|
||||
|
||||
if (SchedPriority < 1 || SchedPriority > 99) {
|
||||
LOG_FATAL("Bad scheduler priority: %d", SchedPriority);
|
||||
} else {
|
||||
sched.sched_priority = SchedPriority;
|
||||
pmax = sched_get_priority_max(SCHED_FIFO);
|
||||
pmin = sched_get_priority_min(SCHED_FIFO);
|
||||
if ( SchedPriority > pmax ) {
|
||||
sched.sched_priority = pmax;
|
||||
}
|
||||
else if ( SchedPriority < pmin ) {
|
||||
sched.sched_priority = pmin;
|
||||
}
|
||||
if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) {
|
||||
LOG(LOGS_ERR, "sched_setscheduler() failed");
|
||||
}
|
||||
else {
|
||||
DEBUG_LOG("Enabled SCHED_FIFO with priority %d",
|
||||
sched.sched_priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SCHED_SETSCHEDULER */
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
/* Lock the process into RAM so that it will never be swapped out */
|
||||
void SYS_Linux_MemLockAll(int LockAll)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
if (LockAll == 1 ) {
|
||||
/* Make sure that we will be able to lock all the memory we need */
|
||||
/* even after dropping privileges. This does not actually reaerve any memory */
|
||||
rlim.rlim_max = RLIM_INFINITY;
|
||||
rlim.rlim_cur = RLIM_INFINITY;
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
||||
LOG(LOGS_ERR, "setrlimit() failed: not locking into RAM");
|
||||
}
|
||||
else {
|
||||
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
|
||||
LOG(LOGS_ERR, "mlockall() failed");
|
||||
}
|
||||
else {
|
||||
DEBUG_LOG("Successfully locked into RAM");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_MLOCKALL */
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
|
||||
{
|
||||
@@ -695,35 +640,17 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
|
||||
#define PHC_READINGS 10
|
||||
|
||||
static int
|
||||
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
process_phc_readings(struct timespec ts[][3], int n, double precision,
|
||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
|
||||
{
|
||||
struct ptp_sys_offset sys_off;
|
||||
struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
|
||||
double min_delay = 0.0, delays[PHC_READINGS], phc_sum, sys_sum, sys_prec;
|
||||
int i, n;
|
||||
double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
|
||||
int i, combined;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
|
||||
if (n > PTP_MAX_SAMPLES)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
ts1.tv_sec = sys_off.ts[i * 2].sec;
|
||||
ts1.tv_nsec = sys_off.ts[i * 2].nsec;
|
||||
ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
|
||||
ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
|
||||
ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
|
||||
ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
||||
|
||||
sys_tss[i] = ts1;
|
||||
phc_tss[i] = ts2;
|
||||
delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
|
||||
for (i = 0; i < n; i++) {
|
||||
delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
|
||||
|
||||
if (delays[i] < 0.0) {
|
||||
/* Step in the middle of a PHC reading? */
|
||||
@@ -738,23 +665,92 @@ get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
sys_prec = LCL_GetSysPrecisionAsQuantum();
|
||||
|
||||
/* Combine best readings */
|
||||
for (i = n = 0, phc_sum = sys_sum = 0.0; i < PHC_READINGS; i++) {
|
||||
for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
|
||||
if (delays[i] > min_delay + MAX(sys_prec, precision))
|
||||
continue;
|
||||
|
||||
phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
|
||||
sys_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
|
||||
n++;
|
||||
phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
|
||||
sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
|
||||
combined++;
|
||||
}
|
||||
|
||||
assert(n);
|
||||
assert(combined);
|
||||
|
||||
UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
|
||||
UTI_AddDoubleToTimespec(&sys_tss[0], sys_sum / n, sys_ts);
|
||||
UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
|
||||
UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
|
||||
*err = MAX(min_delay / 2.0, precision);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
{
|
||||
struct timespec ts[PHC_READINGS][3];
|
||||
struct ptp_sys_offset sys_off;
|
||||
int i;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
|
||||
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
|
||||
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
|
||||
ts[i][1].tv_nsec = sys_off.ts[i * 2 + 1].nsec;
|
||||
ts[i][2].tv_sec = sys_off.ts[i * 2 + 2].sec;
|
||||
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
||||
}
|
||||
|
||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
{
|
||||
#ifdef PTP_SYS_OFFSET_EXTENDED
|
||||
struct timespec ts[PHC_READINGS][3];
|
||||
struct ptp_sys_offset_extended sys_off;
|
||||
int i;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
ts[i][0].tv_sec = sys_off.ts[i][0].sec;
|
||||
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
|
||||
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
|
||||
ts[i][1].tv_nsec = sys_off.ts[i][1].nsec;
|
||||
ts[i][2].tv_sec = sys_off.ts[i][2].sec;
|
||||
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
|
||||
}
|
||||
|
||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
@@ -828,6 +824,10 @@ SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mod
|
||||
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
*reading_mode = 2;
|
||||
return 1;
|
||||
} else if ((*reading_mode == 3 || !*reading_mode) &&
|
||||
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
*reading_mode = 3;
|
||||
return 1;
|
||||
} else if ((*reading_mode == 1 || !*reading_mode) &&
|
||||
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
*reading_mode = 1;
|
||||
|
||||
@@ -35,10 +35,6 @@ extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control);
|
||||
|
||||
extern void SYS_Linux_EnableSystemCallFilter(int level);
|
||||
|
||||
extern void SYS_Linux_MemLockAll(int LockAll);
|
||||
|
||||
extern void SYS_Linux_SetScheduler(int SchedPriority);
|
||||
|
||||
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
|
||||
|
||||
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
|
||||
|
||||
42
sys_macosx.c
42
sys_macosx.c
@@ -49,10 +49,8 @@
|
||||
#ifdef HAVE_MACOS_SYS_TIMEX
|
||||
#include <dlfcn.h>
|
||||
#include "sys_netbsd.h"
|
||||
#include "sys_timex.h"
|
||||
|
||||
static int have_ntp_adjtime = 0;
|
||||
static int have_bad_adjtime = 0;
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
@@ -453,45 +451,13 @@ legacy_MacOSX_Finalise(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#ifdef HAVE_MACOS_SYS_TIMEX
|
||||
/*
|
||||
Test adjtime() to see if Apple have fixed the signed/unsigned bug
|
||||
*/
|
||||
static int
|
||||
test_adjtime()
|
||||
{
|
||||
struct timeval tv1 = {-1, 0};
|
||||
struct timeval tv2 = {0, 0};
|
||||
struct timeval tv;
|
||||
|
||||
if (PRV_AdjustTime(&tv1, &tv) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (PRV_AdjustTime(&tv2, &tv) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (tv.tv_sec < -1 || tv.tv_sec > 1) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_MacOSX_Initialise(void)
|
||||
{
|
||||
#ifdef HAVE_MACOS_SYS_TIMEX
|
||||
have_ntp_adjtime = (dlsym(RTLD_NEXT, "ntp_adjtime") != NULL);
|
||||
if (have_ntp_adjtime) {
|
||||
have_bad_adjtime = !test_adjtime();
|
||||
if (have_bad_adjtime) {
|
||||
LOG(LOGS_WARN, "adjtime() is buggy - using timex driver");
|
||||
SYS_Timex_Initialise();
|
||||
} else {
|
||||
SYS_NetBSD_Initialise();
|
||||
}
|
||||
SYS_NetBSD_Initialise();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -505,11 +471,7 @@ SYS_MacOSX_Finalise(void)
|
||||
{
|
||||
#ifdef HAVE_MACOS_SYS_TIMEX
|
||||
if (have_ntp_adjtime) {
|
||||
if (have_bad_adjtime) {
|
||||
SYS_Timex_Finalise();
|
||||
} else {
|
||||
SYS_NetBSD_Finalise();
|
||||
}
|
||||
SYS_NetBSD_Finalise();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
109
sys_posix.c
Normal file
109
sys_posix.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
This module is for POSIX compliant operating systems.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#if defined(HAVE_SETRLIMIT_MEMLOCK)
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#include "sys_posix.h"
|
||||
#include "conf.h"
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "util.h"
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
|
||||
/* Install SCHED_FIFO real-time scheduler with specified priority */
|
||||
void
|
||||
SYS_Posix_SetScheduler(int priority)
|
||||
{
|
||||
struct sched_param sched;
|
||||
int pmax, pmin;
|
||||
|
||||
if (priority < 1 || priority > 99)
|
||||
LOG_FATAL("Bad scheduler priority: %d", priority);
|
||||
|
||||
sched.sched_priority = priority;
|
||||
pmax = sched_get_priority_max(SCHED_FIFO);
|
||||
pmin = sched_get_priority_min(SCHED_FIFO);
|
||||
if (priority > pmax) {
|
||||
sched.sched_priority = pmax;
|
||||
} else if (priority < pmin) {
|
||||
sched.sched_priority = pmin;
|
||||
}
|
||||
|
||||
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched) < 0) {
|
||||
LOG(LOGS_ERR, "pthread_setschedparam() failed");
|
||||
} else {
|
||||
DEBUG_LOG("Enabled SCHED_FIFO with priority %d", sched.sched_priority);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_PTHREAD_SETSCHEDPARAM */
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
/* Lock the process into RAM so that it will never be swapped out */
|
||||
void
|
||||
SYS_Posix_MemLockAll(void)
|
||||
{
|
||||
#if defined(HAVE_SETRLIMIT_MEMLOCK)
|
||||
struct rlimit rlim;
|
||||
|
||||
/* Ensure we can reserve as much as we need */
|
||||
rlim.rlim_max = RLIM_INFINITY;
|
||||
rlim.rlim_cur = RLIM_INFINITY;
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
||||
LOG(LOGS_ERR, "setrlimit() failed");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
|
||||
LOG(LOGS_ERR, "mlockall() failed");
|
||||
} else {
|
||||
DEBUG_LOG("Successfully locked into RAM");
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_MLOCKALL */
|
||||
36
sys_posix.h
Normal file
36
sys_posix.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
The header file for shared Posix functionality
|
||||
*/
|
||||
|
||||
#ifndef GOT_SYS_POSIX_H
|
||||
#define GOT_SYS_POSIX_H
|
||||
|
||||
extern void SYS_Posix_MemLockAll(void);
|
||||
|
||||
extern void SYS_Posix_SetScheduler(int priority);
|
||||
|
||||
#endif /* GOT_SYS_POSIX_H */
|
||||
12
sysincl.h
12
sysincl.h
@@ -36,11 +36,11 @@
|
||||
#include <float.h>
|
||||
#include <glob.h>
|
||||
#include <grp.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pwd.h>
|
||||
#include <resolv.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
@@ -63,14 +63,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>
|
||||
|
||||
@@ -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
87
test/compilation/003-sanitizers
Executable 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
|
||||
@@ -16,7 +16,7 @@ check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
# The following tests need 64-bit time_t
|
||||
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
|
||||
check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
|
||||
|
||||
for year in 1990 2090; do
|
||||
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
|
||||
|
||||
15
test/simulation/012-daemonts
Executable file
15
test/simulation/012-daemonts
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "daemon timestamping"
|
||||
|
||||
export CLKNETSIM_TIMESTAMPING=0
|
||||
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -23,7 +23,7 @@ EOF
|
||||
|
||||
keys=4
|
||||
|
||||
if grep -q 'FEAT_SECHASH 1' ../../config.h; then
|
||||
if check_config_h 'FEAT_SECHASH 1'; then
|
||||
hashes="MD5 SHA1 SHA256 SHA384 SHA512"
|
||||
else
|
||||
hashes="MD5"
|
||||
|
||||
@@ -3,25 +3,32 @@
|
||||
. ./test.common
|
||||
test_start "SHM refclock"
|
||||
|
||||
check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
||||
check_config_h 'FEAT_PHC 1' || test_skip
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
servers=0
|
||||
limit=1000
|
||||
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
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
test_start "allow/deny directives"
|
||||
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
limit=500
|
||||
|
||||
# Note that start_client in clknetsim.bash always adds allow to the config
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
test_start "chronyc"
|
||||
|
||||
check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
refclock_jitter=$jitter
|
||||
client_conf="
|
||||
refclock SHM 0 noselect
|
||||
@@ -63,4 +66,101 @@ 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" \
|
||||
"dfreq 1.0e-3" \
|
||||
"doffset -1.0" \
|
||||
"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" \
|
||||
"serverstats" \
|
||||
"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
|
||||
|
||||
chronyc_conf="dns -n
|
||||
dns +n
|
||||
dns -4
|
||||
dns -6
|
||||
dns -46
|
||||
timeout 200
|
||||
retries 1
|
||||
keygen
|
||||
keygen 10 MD5 128
|
||||
help
|
||||
quit
|
||||
nosuchcommand"
|
||||
|
||||
run_test || test_fail
|
||||
|
||||
check_chronyc_output "^1 (MD5|SHA1) HEX:........................................
|
||||
10 MD5 HEX:................................
|
||||
System clock:.*this help
|
||||
*$" || test_fail
|
||||
|
||||
test_pass
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
test_start "port and acquisitionport directives"
|
||||
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "leap second"
|
||||
|
||||
check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
||||
|
||||
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
|
||||
|
||||
leap=$[2 * 24 * 3600]
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
test_start "cmdmon timestamps"
|
||||
|
||||
# The following tests need 64-bit time_t
|
||||
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
|
||||
check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
limit=2
|
||||
client_server_options="noselect"
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "smoothtime option"
|
||||
|
||||
check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
||||
|
||||
server_strata=2
|
||||
server_conf="smoothtime 400 0.001"
|
||||
server_server_options="minpoll 8"
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
|
||||
test_start "orphan option"
|
||||
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
server_strata=3
|
||||
server_conf="local stratum 5 orphan
|
||||
server 192.168.123.1
|
||||
server 192.168.123.2
|
||||
server 192.168.123.3"
|
||||
max_sync_time=500
|
||||
client_start=140
|
||||
chronyc_start=300
|
||||
chronyc_conf="tracking"
|
||||
time_rms_limit=5e-4
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "tai option"
|
||||
|
||||
check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
||||
|
||||
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 31 2008 23:50:00' +'%s')
|
||||
|
||||
leap=$[10 * 60]
|
||||
|
||||
19
test/simulation/127-filter
Executable file
19
test/simulation/127-filter
Executable 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
|
||||
27
test/simulation/128-nocontrol
Executable file
27
test/simulation/128-nocontrol
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "-x option"
|
||||
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
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
|
||||
26
test/simulation/129-reload
Executable file
26
test/simulation/129-reload
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/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
|
||||
maxupdateskew 10000"
|
||||
|
||||
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
23
test/simulation/130-quit
Executable 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
20
test/simulation/131-maxchange
Executable 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
21
test/simulation/132-logchange
Executable 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
|
||||
37
test/simulation/133-hwtimestamp
Executable file
37
test/simulation/133-hwtimestamp
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "hwtimestamp directive"
|
||||
|
||||
check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
|
||||
|
||||
export CLKNETSIM_TIMESTAMPING=2
|
||||
|
||||
refclock_jitter=1e-8
|
||||
refclock_offset=10.0
|
||||
min_sync_time=4
|
||||
max_sync_time=20
|
||||
limit=200
|
||||
server_conf="hwtimestamp eth0"
|
||||
client_server_options="minpoll 0 maxpoll 0 minsamples 32 xleave"
|
||||
client_chronyd_options="-d"
|
||||
|
||||
for client_conf in "hwtimestamp eth0" "hwtimestamp eth0
|
||||
acquisitionport 123"; do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
if check_config_h 'FEAT_DEBUG 1'; 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
|
||||
done
|
||||
|
||||
test_pass
|
||||
33
test/simulation/134-log
Executable file
33
test/simulation/134-log
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "log directive"
|
||||
|
||||
check_config_h 'FEAT_PHC 1' || test_skip
|
||||
|
||||
refclock_jitter=$jitter
|
||||
client_server_options="maxpoll 6"
|
||||
client_conf="refclock PHC /dev/ptp0 dpoll 4 poll 6 noselect
|
||||
logbanner 10
|
||||
logdir tmp
|
||||
log tracking rawmeasurements measurements statistics rtc refclocks tempcomp
|
||||
tempcomp tmp/tempcomp 64 0.0 0.0 0.0 0.0"
|
||||
|
||||
echo 0.0 > tmp/tempcomp
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_file_messages "=============" 31 33 \
|
||||
tracking.log measurements.log tempcomp.log || test_fail
|
||||
check_file_messages "20.*192\.168\.123\.1" 150 160 \
|
||||
tracking.log measurements.log statistics.log || test_fail
|
||||
check_file_messages "20.*PHC0" 150 160 statistics.log || test_fail
|
||||
check_file_messages "20.*PHC0" 750 800 refclocks.log || test_fail
|
||||
check_file_messages "20.* 0\.0000" 150 160 tempcomp.log || test_fail
|
||||
|
||||
test_pass
|
||||
18
test/simulation/135-ratelimit
Executable file
18
test/simulation/135-ratelimit
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
test_start "ratelimit directive"
|
||||
|
||||
server_conf="ratelimit interval 6 burst 2 leak 4"
|
||||
client_server_options="minpoll 3 maxpoll 3"
|
||||
min_sync_time=16
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_file_messages " 2 1 " 1200 1300 log.packets || test_fail
|
||||
check_file_messages " 1 2 " 180 220 log.packets || test_fail
|
||||
|
||||
test_pass
|
||||
16
test/simulation/136-broadcast
Executable file
16
test/simulation/136-broadcast
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "broadcast directive"
|
||||
|
||||
server_conf="broadcast 64 192.168.123.255"
|
||||
client_server_options="offline"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval && test_fail
|
||||
|
||||
check_file_messages " 1 2 " 150 160 log.packets || test_fail
|
||||
|
||||
test_pass
|
||||
@@ -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,15 +76,16 @@ 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() {
|
||||
rm -f tmp/*
|
||||
echo "Testing $@:"
|
||||
|
||||
check_config_h 'FEAT_NTP 1' || test_skip
|
||||
}
|
||||
|
||||
test_pass() {
|
||||
@@ -211,6 +213,12 @@ get_chronyd_conf() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if chrony was built with specified option in config.h
|
||||
check_config_h() {
|
||||
local pattern=$1
|
||||
grep -q "^#define $pattern" ../../config.h
|
||||
}
|
||||
|
||||
# Check if the clock was well synchronized
|
||||
check_sync() {
|
||||
local i sync_time max_time_error max_freq_error ret=0
|
||||
@@ -253,7 +261,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 +345,43 @@ check_chronyc_output() {
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Check the number of messages matching a pattern 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 the number of messages matching a pattern in a specified file
|
||||
check_file_messages() {
|
||||
local i count ret=0 pattern=$1 min=$2 max=$3
|
||||
shift 3
|
||||
|
||||
test_message 2 1 "checking number of messages \"$pattern\":"
|
||||
|
||||
for i; do
|
||||
count=$(grep "$pattern" tmp/$i | wc -l)
|
||||
test_message 3 0 "$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 +403,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 +426,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 +455,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 +478,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
|
||||
|
||||
13
test/system/001-minimal
Executable file
13
test/system/001-minimal
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "minimal configuration"
|
||||
|
||||
minimal_config=1
|
||||
|
||||
start_chronyd || test_fail
|
||||
stop_chronyd || test_fail
|
||||
check_chronyd_messages || test_fail
|
||||
|
||||
test_pass
|
||||
13
test/system/002-extended
Executable file
13
test/system/002-extended
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "extended configuration"
|
||||
|
||||
start_chronyd || test_fail
|
||||
wait_for_sync || test_fail
|
||||
stop_chronyd || test_fail
|
||||
check_chronyd_messages || test_fail
|
||||
check_chronyd_files || test_fail
|
||||
|
||||
test_pass
|
||||
15
test/system/003-memlock
Executable file
15
test/system/003-memlock
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "memory locking"
|
||||
|
||||
extra_chronyd_options="-m"
|
||||
|
||||
start_chronyd || test_fail
|
||||
wait_for_sync || test_fail
|
||||
stop_chronyd || test_fail
|
||||
check_chronyd_messages || test_fail
|
||||
check_chronyd_files || test_fail
|
||||
|
||||
test_pass
|
||||
15
test/system/004-priority
Executable file
15
test/system/004-priority
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "process priority"
|
||||
|
||||
extra_chronyd_options="-P 1"
|
||||
|
||||
start_chronyd || test_fail
|
||||
wait_for_sync || test_fail
|
||||
stop_chronyd || test_fail
|
||||
check_chronyd_messages || test_fail
|
||||
check_chronyd_files || test_fail
|
||||
|
||||
test_pass
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user