summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Blut <vincent.debian@free.fr>2016-12-09 23:43:31 +0100
committerVincent Blut <vincent.debian@free.fr>2016-12-09 23:43:31 +0100
commit134fb69072f998f489b020c4eabe0e99ac8fa2ac (patch)
tree6c3393c756f96699c4dbbbae1d09ddbbd1ee5015
parent53e3fdbcd5a0daa3dba21532d87fe65c048a9dd9 (diff)
New upstream version 3.0-pre1
-rw-r--r--FAQ41
-rw-r--r--INSTALL2
-rw-r--r--Makefile.in6
-rw-r--r--NEWS26
-rw-r--r--README45
-rw-r--r--addressing.h1
-rw-r--r--candm.h70
-rw-r--r--client.c454
-rw-r--r--clientlog.c60
-rw-r--r--clientlog.h8
-rw-r--r--cmdmon.c116
-rw-r--r--cmdparse.c310
-rw-r--r--cmdparse.h25
-rw-r--r--conf.c86
-rw-r--r--conf.h5
-rwxr-xr-xconfigure130
-rw-r--r--contrib/bryan_christianson_1/README.txt8
-rw-r--r--doc/Makefile.in8
-rw-r--r--doc/chrony.conf.adoc266
-rw-r--r--doc/chrony.conf.man.in353
-rw-r--r--doc/chronyc.adoc104
-rw-r--r--doc/chronyc.man.in149
-rw-r--r--doc/chronyd.adoc30
-rw-r--r--doc/chronyd.man.in36
-rw-r--r--doc/faq.adoc43
-rw-r--r--examples/chrony.spec2
-rw-r--r--hwclock.c204
-rw-r--r--hwclock.h48
-rw-r--r--keys.c103
-rw-r--r--keys.h5
-rw-r--r--local.c85
-rw-r--r--local.h15
-rw-r--r--localp.h2
-rw-r--r--logging.c12
-rw-r--r--logging.h7
-rw-r--r--main.c33
-rw-r--r--manual.c26
-rw-r--r--manual.h2
-rw-r--r--nameserv_async.c6
-rw-r--r--ntp.h4
-rw-r--r--ntp_core.c931
-rw-r--r--ntp_core.h36
-rw-r--r--ntp_io.c328
-rw-r--r--ntp_io.h3
-rw-r--r--ntp_io_linux.c578
-rw-r--r--ntp_io_linux.h37
-rw-r--r--ntp_signd.c380
-rw-r--r--ntp_signd.h44
-rw-r--r--ntp_sources.c65
-rw-r--r--ntp_sources.h12
-rw-r--r--pktlength.c8
-rw-r--r--refclock.c103
-rw-r--r--refclock.h7
-rw-r--r--refclock_phc.c15
-rw-r--r--refclock_pps.c11
-rw-r--r--refclock_shm.c24
-rw-r--r--refclock_sock.c17
-rw-r--r--reference.c78
-rw-r--r--reference.h8
-rw-r--r--regress.c72
-rw-r--r--regress.h12
-rw-r--r--reports.h37
-rw-r--r--rtc.c2
-rw-r--r--rtc_linux.c50
-rw-r--r--sched.c284
-rw-r--r--sched.h20
-rw-r--r--smooth.c24
-rw-r--r--smooth.h10
-rw-r--r--sources.c264
-rw-r--r--sources.h39
-rw-r--r--sourcestats.c259
-rw-r--r--sourcestats.h18
-rw-r--r--srcparams.h6
-rw-r--r--stubs.c37
-rw-r--r--sys_generic.c30
-rw-r--r--sys_linux.c70
-rw-r--r--sys_linux.h2
-rw-r--r--sys_macosx.c63
-rw-r--r--sys_macosx.h2
-rw-r--r--sys_netbsd.c11
-rw-r--r--tempcomp.c6
-rwxr-xr-xtest/compilation/001-features3
-rwxr-xr-xtest/compilation/002-scanbuild14
-rwxr-xr-xtest/simulation/010-multrecv17
-rwxr-xr-xtest/simulation/011-asymjitter18
-rwxr-xr-xtest/simulation/105-ntpauth16
-rwxr-xr-xtest/simulation/110-chronyc2
-rwxr-xr-xtest/simulation/113-leapsecond18
-rwxr-xr-xtest/simulation/114-presend2
-rwxr-xr-xtest/simulation/119-smoothtime3
-rwxr-xr-xtest/simulation/122-xleave36
-rw-r--r--test/simulation/test.common16
-rw-r--r--test/unit/clientlog.c18
-rw-r--r--test/unit/hwclock.c72
-rw-r--r--test/unit/smooth.c14
-rw-r--r--test/unit/sources.c10
-rw-r--r--test/unit/test.c2
-rw-r--r--test/unit/util.c140
-rw-r--r--util.c386
-rw-r--r--util.h91
-rw-r--r--version.txt2
101 files changed, 5665 insertions, 2154 deletions
diff --git a/FAQ b/FAQ
index e3a2fde..cbac642 100644
--- a/FAQ
+++ b/FAQ
@@ -81,8 +81,8 @@ which would take a very long time. The makestep directive does that.
In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialized on the
next boot from the RTC, the rtcsync directive enables a mode in which the
-system time is periodically copied to the RTC. It is supported on Linux and Mac
-OS X.
+system time is periodically copied to the RTC. It is supported on Linux and
+macOS.
If you want to use public NTP servers from the pool.ntp.org project, the
minimal chrony.conf file could be:
@@ -165,13 +165,18 @@ network. It's better to use more than one server, three or four is usually
recommended as the minimum, so chronyd can detect servers that serve false time
and combine measurements from multiple sources.
+If you have a network card with hardware timestamping supported on Linux, it
+can be enabled by the hwtimestamp directive in the chrony.conf file. It should
+make local receive and transmit timestamps of NTP packets much more accurate.
+
There are also useful options which can be set in the server directive, they
-are minpoll, maxpoll, polltarget, maxdelay, maxdelayratio and maxdelaydevratio.
+are minpoll, maxpoll, polltarget, maxdelay, maxdelayratio, maxdelaydevratio,
+and xleave.
The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their
default values are 6 (64 seconds) for minpoll, 10 (1024 seconds) for maxpoll
-and 6 (samples) for polltarget. The default values should be used for general
+and 8 (samples) for polltarget. The default values should be used for general
servers on the Internet. With your own NTP servers or if have permission to
poll some servers more frequently, setting these options for shorter polling
intervals may significantly improve the accuracy of the system clock.
@@ -198,6 +203,14 @@ local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
+If your server supports the interleaved mode, the xleave option should be added
+to the server directive in order to receive server's more accurate hardware or
+kernel transmit timestamps. When combined with local hardware timestamping, a
+sub-microsecond accuracy may be possible. An example could be
+
+server ntp.local minpoll 2 maxpoll 2 xleave
+hwtimestamp eth0
+
2.7. What happened to the commandkey and generatecommandkey directives?
They were removed in version 2.2. Authentication is no longer supported in the
@@ -289,12 +302,15 @@ directive).
4.3. Why does chronyc tracking always print an IPv4 address as reference ID?
-The reference ID is a 32-bit value and is always printed in quad-dotted
-notation, even if the reference source doesn't have an IPv4 address. For IPv4
-addresses, the reference ID is equal to the address, but for IPv6 addresses it
-is the first 32 bits of the MD5 sum of the address. For reference clocks, the
-reference ID is the value specified with the refid option in the refclock
-directive.
+The reference ID is a 32-bit value and in versions before 3.0 it was printed in
+quad-dotted notation, even if the reference source did not actually have an
+IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
+for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
+reference clocks, the reference ID is the value specified with the refid option
+in the refclock directive.
+
+Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
+confusion with IPv4 addresses.
If you need to get the IP address of the current reference source, use the -n
option to disable resolving of IP addresses and read the second field (printed
@@ -376,7 +392,8 @@ serve time to clients in the network which support the broadcast client mode
6.3. Can chronyd keep the system clock a fixed offset away from real time?
-This is not possible as the program currently stands.
+Yes. Starting from version 3.0, an offset can be specified by the offset option
+for all time sources in the chrony.conf file.
6.4. What happens if the network connection is dropped without using chronyc's
offline command first?
@@ -406,4 +423,4 @@ needs to be made to work as a service.
We have no plans to do this. Anyone is welcome to pick this work up and
contribute it back to the project.
-Last updated 2016-11-21 12:03:45 CET
+Last updated 2016-12-09 09:04:25 CET
diff --git a/INSTALL b/INSTALL
index 2a106ff..216a9b4 100644
--- a/INSTALL
+++ b/INSTALL
@@ -147,4 +147,4 @@ tar cvf - . | gzip -9 > chrony.tar.gz
to build a package. When untarred within the root directory, this will install
the files to the intended final locations.
-Last updated 2016-11-21 12:03:45 CET
+Last updated 2016-12-09 09:04:25 CET
diff --git a/Makefile.in b/Makefile.in
index ea47830..058f6c2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -63,12 +63,6 @@ chronyd : $(OBJS) $(EXTRA_OBJS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
-client.o : client.c
- $(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
-
-$(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
- $(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $<
-
distclean : clean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
diff --git a/NEWS b/NEWS
index 4f5c218..16e18e4 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,29 @@
+New in version 3.0
+==================
+
+Enhancements
+------------
+* Add support for software and hardware timestamping on Linux
+* Add support for client/server and symmetric interleaved modes
+* Add support for MS-SNTP authentication in Samba
+* Add support for truncated MACs in NTPv4 packets
+* Estimate and correct for asymmetric network jitter
+* Increase default minsamples and polltarget to improve stability
+ with very low jitter
+* Add maxjitter directive to limit source selection by jitter
+* Add offset option to server/pool/peer directive
+* Add maxlockage option to refclock directive
+* Add -t option to chronyd to exit after specified time
+* Add partial protection against replay attacks on symmetric mode
+* Don't reset polling interval when switching sources to online state
+* Improve maximum server throughput on Linux and NetBSD
+* Remove dump files after start
+* Add tab-completion to chronyc with libedit/readline
+* Add ntpdata command to print details about NTP measurements
+* Indicate truncated addresses/hostnames in chronyc output
+* Print reference IDs as hexadecimal numbers to avoid confusion with
+ IPv4 addresses
+
New in version 2.4.1
====================
diff --git a/README b/README
index 9c59f3a..7d03d3c 100644
--- a/README
+++ b/README
@@ -14,10 +14,10 @@ intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
-Typical accuracy between two machines on a LAN is in tens, or a few
-hundreds, of microseconds; over the Internet, accuracy is typically
-within a few milliseconds. With a good hardware reference clock
-sub-microsecond accuracy is possible.
+Typical accuracy between two machines synchronised over the Internet is
+within a few milliseconds; on a LAN, accuracy is typically in tens of
+microseconds. With hardware timestamping or a hardware reference clock
+sub-microsecond accuracy may be possible.
Two programs are included in chrony, chronyd is a daemon that can be
started at boot time and chronyc is a command-line interface program
@@ -27,7 +27,7 @@ operating parameters whilst it is running.
What will chrony run on?
========================
-The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and
+The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one
of the existing system-specific drivers and look into the quirks of
@@ -95,19 +95,12 @@ License
chrony is distributed under the GNU General Public License version 2.
-
-Author
-======
+Authors
+=======
Richard P. Curnow <rc@rc0.org.uk>
-
-
-Maintainers
-===========
-
Miroslav Lichvar <mlichvar@redhat.com>
-
Acknowledgements
================
@@ -118,6 +111,9 @@ implementation has been used to check the details of the protocol.
The following people have provided patches and other major contributions
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
@@ -139,7 +135,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Entries in contrib directory
Bryan Christianson <bryan@whatroute.net>
- Support for Mac OS X
+ Support for macOS
Support for privilege separation
Entries in contrib directory
@@ -198,18 +194,6 @@ Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi>
Advice on configuring for BSD/386
-Miroslav Lichvar <mlichvar@redhat.com>
- Reference clock support
- IPv6 support
- Linux capabilities support
- Leap second support
- Improved source selection
- Improved sample history trimming
- Improved polling interval adjustment
- Improved stability with temporary asymmetric delays
- Temperature compensation
- Many other bug fixes and improvements
-
Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100
@@ -228,6 +212,9 @@ Andreas Piesk <apiesk@virbus.de>
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
@@ -244,5 +231,5 @@ Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86
-Many other people have contributed bug reports and suggestions. I'm
-sorry I can't identify all of you individually.
+Many other people have contributed bug reports and suggestions. We are sorry
+we cannot identify all of you individually.
diff --git a/addressing.h b/addressing.h
index 863aca9..e7a68b1 100644
--- a/addressing.h
+++ b/addressing.h
@@ -42,6 +42,7 @@ typedef struct {
uint8_t in6[16];
} addr;
uint16_t family;
+ uint16_t _pad;
} IPAddr;
typedef struct {
diff --git a/candm.h b/candm.h
index 05daaeb..75425b9 100644
--- a/candm.h
+++ b/candm.h
@@ -94,14 +94,17 @@
#define REQ_SERVER_STATS 54
#define REQ_CLIENT_ACCESSES_BY_INDEX2 55
#define REQ_LOCAL2 56
-#define N_REQUEST_TYPES 57
+#define REQ_NTP_DATA 57
+#define REQ_ADD_SERVER2 58
+#define REQ_ADD_PEER2 59
+#define N_REQUEST_TYPES 60
-/* Structure used to exchange timevals independent on size of time_t */
+/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
uint32_t tv_sec_high;
uint32_t tv_sec_low;
uint32_t tv_nsec;
-} Timeval;
+} Timespec;
/* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff
@@ -200,12 +203,12 @@ typedef struct {
} REQ_Modify_Makestep;
typedef struct {
- Timeval ts;
+ Timespec ts;
int32_t EOR;
} REQ_Logon;
typedef struct {
- Timeval ts;
+ Timespec ts;
int32_t EOR;
} REQ_Settime;
@@ -246,6 +249,7 @@ typedef struct {
#define REQ_ADDSRC_NOSELECT 0x10
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
+#define REQ_ADDSRC_INTERLEAVED 0x80
typedef struct {
IPAddr ip_addr;
@@ -253,9 +257,17 @@ typedef struct {
int32_t minpoll;
int32_t maxpoll;
int32_t presend_minpoll;
+ uint32_t min_stratum;
+ uint32_t poll_target;
+ uint32_t version;
+ uint32_t max_sources;
+ int32_t min_samples;
+ int32_t max_samples;
uint32_t authkey;
Float max_delay;
Float max_delay_ratio;
+ Float max_delay_dev_ratio;
+ Float offset;
uint32_t flags;
int32_t EOR;
} REQ_NTP_Source;
@@ -309,6 +321,11 @@ typedef struct {
int32_t EOR;
} REQ_SmoothTime;
+typedef struct {
+ IPAddr ip_addr;
+ int32_t EOR;
+} REQ_NTPData;
+
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -409,6 +426,7 @@ typedef struct {
REQ_ManualDelete manual_delete;
REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
+ REQ_NTPData ntp_data;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -442,7 +460,8 @@ typedef struct {
#define RPY_SMOOTHING 13
#define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
-#define N_REPLY_TYPES 16
+#define RPY_NTP_DATA 16
+#define N_REPLY_TYPES 17
/* Status codes */
#define STT_SUCCESS 0
@@ -512,7 +531,7 @@ typedef struct {
IPAddr ip_addr;
uint16_t stratum;
uint16_t leap_status;
- Timeval ref_time;
+ Timespec ref_time;
Float current_correction;
Float last_offset;
Float rms_offset;
@@ -540,7 +559,7 @@ typedef struct {
} RPY_Sourcestats;
typedef struct {
- Timeval ref_time;
+ Timespec ref_time;
uint16_t n_samples;
uint16_t n_runs;
uint32_t span_seconds;
@@ -590,7 +609,7 @@ typedef struct {
#define MAX_MANUAL_LIST_SAMPLES 16
typedef struct {
- Timeval when;
+ Timespec when;
Float slewed_offset;
Float orig_offset;
Float residual;
@@ -624,6 +643,38 @@ typedef struct {
int32_t EOR;
} RPY_Smoothing;
+#define RPY_NTP_FLAGS_TESTS 0x3ff
+#define RPY_NTP_FLAG_INTERLEAVED 0x4000
+#define RPY_NTP_FLAG_AUTHENTICATED 0x8000
+
+typedef struct {
+ IPAddr remote_addr;
+ IPAddr local_addr;
+ uint16_t remote_port;
+ uint8_t leap;
+ uint8_t version;
+ uint8_t mode;
+ uint8_t stratum;
+ int8_t poll;
+ int8_t precision;
+ Float root_delay;
+ Float root_dispersion;
+ uint32_t ref_id;
+ Timespec ref_time;
+ Float offset;
+ Float peer_delay;
+ Float peer_dispersion;
+ Float response_time;
+ Float jitter_asymmetry;
+ uint16_t flags;
+ uint8_t tx_tss_char;
+ uint8_t rx_tss_char;
+ uint32_t total_tx_count;
+ uint32_t total_rx_count;
+ uint32_t total_valid_count;
+ int32_t EOR;
+} RPY_NTPData;
+
typedef struct {
uint8_t version;
uint8_t pkt_type;
@@ -652,6 +703,7 @@ typedef struct {
RPY_ManualList manual_list;
RPY_Activity activity;
RPY_Smoothing smoothing;
+ RPY_NTPData ntp_data;
} data; /* Reply specific parameters */
} CMD_Reply;
diff --git a/client.c b/client.c
index 314b3a3..ba3111a 100644
--- a/client.c
+++ b/client.c
@@ -94,8 +94,11 @@ void LOG_Message(LOG_Severity severity,
}
/* ================================================== */
-/* Read a single line of commands from standard input. Eventually we
- might want to use the GNU readline library. */
+/* Read a single line of commands from standard input */
+
+#ifdef FEAT_READLINE
+static char **command_name_completion(const char *text, int start, int end);
+#endif
static char *
read_line(void)
@@ -107,6 +110,9 @@ read_line(void)
#ifdef FEAT_READLINE
char *cmd;
+ rl_attempted_completion_function = command_name_completion;
+ rl_basic_word_break_characters = "\t\n\r";
+
/* save line only if not empty */
cmd = readline(prompt);
if( cmd == NULL ) return( NULL );
@@ -1059,51 +1065,24 @@ static int
process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
{
CPS_NTP_Source data;
- CPS_Status status;
IPAddr ip_addr;
- char str[64];
- int result = 0;
+ int result = 0, status;
+ const char *opt_name;
status = CPS_ParseNTPSourceAdd(line, &data);
switch (status) {
- case CPS_Success:
+ case 0:
+ LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for add command");
+ break;
+ default:
if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Invalid host/IP address");
break;
}
- if (data.params.min_stratum != SRC_DEFAULT_MINSTRATUM) {
- LOG(LOGS_WARN, LOGF_Client, "Option minstratum not supported");
- break;
- }
-
- if (data.params.poll_target != SRC_DEFAULT_POLLTARGET) {
- LOG(LOGS_WARN, LOGF_Client, "Option polltarget not supported");
- break;
- }
-
- if (data.params.max_delay_dev_ratio != SRC_DEFAULT_MAXDELAYDEVRATIO) {
- LOG(LOGS_WARN, LOGF_Client, "Option maxdelaydevratio not supported");
- break;
- }
-
- if (data.params.version != NTP_VERSION) {
- LOG(LOGS_WARN, LOGF_Client, "Option version not supported");
- break;
- }
-
- if (data.params.max_sources != SRC_DEFAULT_MAXSOURCES) {
- LOG(LOGS_WARN, LOGF_Client, "Option maxsources not supported");
- break;
- }
-
- if (data.params.min_samples != SRC_DEFAULT_MINSAMPLES) {
- LOG(LOGS_WARN, LOGF_Client, "Option minsamples not supported");
- break;
- }
-
- if (data.params.max_samples != SRC_DEFAULT_MAXSAMPLES) {
- LOG(LOGS_WARN, LOGF_Client, "Option maxsamples not supported");
+ opt_name = NULL;
+ if (opt_name) {
+ LOG(LOGS_ERR, LOGF_Client, "%s can't be set in chronyc", opt_name);
break;
}
@@ -1112,13 +1091,23 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll);
msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll);
+ msg->data.ntp_source.min_stratum = htonl(data.params.min_stratum);
+ msg->data.ntp_source.poll_target = htonl(data.params.poll_target);
+ msg->data.ntp_source.version = htonl(data.params.version);
+ msg->data.ntp_source.max_sources = htonl(data.params.max_sources);
+ msg->data.ntp_source.min_samples = htonl(data.params.min_samples);
+ msg->data.ntp_source.max_samples = htonl(data.params.max_samples);
msg->data.ntp_source.authkey = htonl(data.params.authkey);
msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay);
msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio);
+ msg->data.ntp_source.max_delay_dev_ratio =
+ UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio);
+ msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
msg->data.ntp_source.flags = htonl(
(data.params.online ? REQ_ADDSRC_ONLINE : 0) |
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
+ (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
@@ -1126,10 +1115,6 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
result = 1;
break;
- default:
- CPS_StatusToString(status, str, sizeof (str));
- LOG(LOGS_ERR, LOGF_Client, "%s", str);
- break;
}
return result;
@@ -1140,7 +1125,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
static int
process_cmd_add_server(CMD_Request *msg, char *line)
{
- msg->command = htons(REQ_ADD_SERVER);
+ msg->command = htons(REQ_ADD_SERVER2);
return process_cmd_add_server_or_peer(msg, line);
}
@@ -1149,7 +1134,7 @@ process_cmd_add_server(CMD_Request *msg, char *line)
static int
process_cmd_add_peer(CMD_Request *msg, char *line)
{
- msg->command = htons(REQ_ADD_PEER);
+ msg->command = htons(REQ_ADD_PEER2);
return process_cmd_add_server_or_peer(msg, line);
}
@@ -1206,6 +1191,7 @@ give_help(void)
"\0\0"
"NTP sources:\0\0"
"activity\0Check how many NTP sources are online/offline\0"
+ "ntpdata <address>\0Display information about last valid measurement\0"
"add server <address> [options]\0Add new NTP server\0"
"add peer <address> [options]\0Add new NTP peer\0"
"delete <address>\0Remove server or peer\0"
@@ -1275,8 +1261,53 @@ give_help(void)
}
/* ================================================== */
+/* Tab-completion when editline/readline is available */
+
+#ifdef FEAT_READLINE
+static char *
+command_name_generator(const char *text, int state)
+{
+ const char *name, *names[] = {
+ "accheck", "activity", "add peer", "add server", "allow", "burst",
+ "clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
+ "deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
+ "manual on", "manual off", "manual delete", "manual list", "manual reset",
+ "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
+ "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
+ "polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
+ "retries", "rtcdata", "serverstats", "settime", "smoothing", "smoothtime",
+ "sources", "sources -v", "sourcestats", "sourcestats -v", "timeout",
+ "tracking", "trimrtc", "waitsync", "writertc",
+ NULL
+ };
+ static int list_index, len;
+
+ if (!state) {
+ list_index = 0;
+ len = strlen(text);
+ }
+
+ while ((name = names[list_index++])) {
+ if (strncmp(name, text, len) == 0) {
+ return strdup(name);
+ }
+ }
+
+ return NULL;
+}
+
+/* ================================================== */
+
+static char **
+command_name_completion(const char *text, int start, int end)
+{
+ rl_attempted_completion_over = 1;
+ return rl_completion_matches(text, command_name_generator);
+}
+#endif
+
+/* ================================================== */
-static unsigned long sequence = 0;
static int max_retries = 2;
static int initial_timeout = 1000;
static int proto_version = PROTO_VERSION_NUMBER;
@@ -1289,7 +1320,6 @@ static int proto_version = PROTO_VERSION_NUMBER;
static int
submit_request(CMD_Request *request, CMD_Reply *reply)
{
- unsigned long tx_sequence;
int bad_length, bad_sequence, bad_header;
int select_status;
int recv_status;
@@ -1297,52 +1327,72 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
int expected_length;
int command_length;
int padding_length;
+ struct timespec ts_now, ts_start;
struct timeval tv;
- int timeout;
- int n_attempts;
+ int n_attempts, new_attempt;
+ double timeout;
fd_set rdfd, wrfd, exfd;
request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0;
request->res2 = 0;
- tx_sequence = sequence++;
- request->sequence = htonl(tx_sequence);
- request->attempt = 0;
request->pad1 = 0;
request->pad2 = 0;
- timeout = initial_timeout;
-
n_attempts = 0;
+ new_attempt = 1;
do {
- request->version = proto_version;
- command_length = PKL_CommandLength(request);
- padding_length = PKL_CommandPaddingLength(request);
- assert(command_length > 0 && command_length > padding_length);
+ if (new_attempt) {
+ new_attempt = 0;
- /* Zero the padding to avoid sending uninitialized data */
- memset(((char *)request) + command_length - padding_length, 0, padding_length);
+ if (n_attempts > max_retries)
+ return 0;
- if (sock_fd < 0) {
- DEBUG_LOG(LOGF_Client, "No socket to send request");
- return 0;
+ if (gettimeofday(&tv, NULL))
+ return 0;
+
+ UTI_TimevalToTimespec(&tv, &ts_start);
+
+ UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
+ request->attempt = htons(n_attempts);
+ request->version = proto_version;
+ command_length = PKL_CommandLength(request);
+ padding_length = PKL_CommandPaddingLength(request);
+ assert(command_length > 0 && command_length > padding_length);
+
+ n_attempts++;
+
+ /* Zero the padding to not send any uninitialized data */
+ memset(((char *)request) + command_length - padding_length, 0, padding_length);
+
+ if (sock_fd < 0) {
+ DEBUG_LOG(LOGF_Client, "No socket to send request");
+ return 0;
+ }
+
+ if (send(sock_fd, (void *)request, command_length, 0) < 0) {
+ DEBUG_LOG(LOGF_Client, "Could not send %d bytes : %s",
+ command_length, strerror(errno));
+ return 0;
+ }
+
+ DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length);
}
- if (send(sock_fd, (void *)request, command_length, 0) < 0) {
- DEBUG_LOG(LOGF_Client, "Could not send %d bytes : %s",
- command_length, strerror(errno));
+ if (gettimeofday(&tv, NULL))
return 0;
- }
- DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length);
+ UTI_TimevalToTimespec(&tv, &ts_now);
- /* Increment this for next time */
- ++ request->attempt;
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = timeout % 1000 * 1000;
- timeout *= 2;
+ /* Check if the clock wasn't stepped back */
+ if (UTI_CompareTimespecs(&ts_now, &ts_start) < 0)
+ ts_start = ts_now;
+
+ timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
+ UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
+ UTI_DoubleToTimeval(timeout, &tv);
+ DEBUG_LOG(LOGF_Client, "Timeout %f seconds", timeout);
FD_ZERO(&rdfd);
FD_ZERO(&wrfd);
@@ -1359,14 +1409,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno));
} else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */
- n_attempts ++;
- if (n_attempts > max_retries) {
- return 0;
- }
-
- /* Back to top of loop to do resend */
- continue;
-
+ new_attempt = 1;
} else {
recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_Reply), 0);
@@ -1374,11 +1417,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
/* If we get connrefused here, it suggests the sendto is
going to a dead port */
DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno));
-
- n_attempts++;
- if (n_attempts > max_retries) {
- return 0;
- }
+ new_attempt = 1;
} else {
DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status);
@@ -1393,16 +1432,12 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
expected_length < offsetof(CMD_Reply, data));
if (!bad_length) {
- bad_sequence = (ntohl(reply->sequence) != tx_sequence);
+ bad_sequence = reply->sequence != request->sequence;
} else {
bad_sequence = 0;
}
if (bad_length || bad_sequence) {
- n_attempts++;
- if (n_attempts > max_retries) {
- return 0;
- }
continue;
}
@@ -1415,10 +1450,6 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
(reply->command != request->command));
if (bad_header) {
- n_attempts++;
- if (n_attempts > max_retries) {
- return 0;
- }
continue;
}
@@ -1429,6 +1460,8 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
if (proto_version == PROTO_VERSION_NUMBER &&
reply->version == PROTO_VERSION_NUMBER - 1) {
proto_version = PROTO_VERSION_NUMBER - 1;
+ n_attempts--;
+ new_attempt = 1;
continue;
}
#else
@@ -1436,9 +1469,8 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#endif
/* Good packet received, print out results */
- DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d seq=%d",
- ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status),
- ntohl(reply->sequence));
+ DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d",
+ ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
break;
}
}
@@ -1554,7 +1586,7 @@ print_seconds(unsigned long s)
if (s == (uint32_t)-1) {
printf(" -");
- } else if (s <= 1024) {
+ } else if (s < 1200) {
printf("%4ld", s);
} else if (s < 36000) {
printf("%3ldm", s / 60);
@@ -1699,7 +1731,7 @@ print_report(const char *format, ...)
unsigned long long_uinteger;
unsigned int uinteger;
int integer;
- struct timeval *tv;
+ struct timespec *ts;
struct tm *tm;
double dbl;
@@ -1780,6 +1812,10 @@ print_report(const char *format, ...)
}
switch (spec) {
+ case 'B': /* boolean */
+ integer = va_arg(ap, int);
+ printf("%s", integer ? "Yes" : "No");
+ break;
case 'C': /* clientlog interval */
integer = va_arg(ap, int);
print_clientlog_interval(integer);
@@ -1795,6 +1831,63 @@ print_report(const char *format, ...)
long_uinteger = va_arg(ap, unsigned long);
print_seconds(long_uinteger);
break;
+ case 'L': /* leap status */
+ integer = va_arg(ap, int);
+ switch (integer) {
+ case LEAP_Normal:
+ string = "Normal";
+ break;
+ case LEAP_InsertSecond:
+ string = "Insert second";
+ break;
+ case LEAP_DeleteSecond:
+ string = "Delete second";
+ break;
+ case LEAP_Unsynchronised:
+ string = "Not synchronised";
+ break;
+ default:
+ string = "Invalid";
+ break;
+ }
+ printf("%s", string);
+ break;
+ case 'M': /* NTP mode */
+ integer = va_arg(ap, int);
+ switch (integer) {
+ case MODE_ACTIVE:
+ string = "Symmetric active";
+ break;
+ case MODE_PASSIVE:
+ string = "Symmetric passive";
+ break;
+ case MODE_SERVER:
+ string = "Server";
+ break;
+ default:
+ string = "Invalid";
+ break;
+ }
+ printf("%s", string);
+ break;
+ case 'N': /* Timestamp source */
+ integer = va_arg(ap, int);
+ switch (integer) {
+ case 'D':
+ string = "Daemon";
+ break;
+ case 'K':
+ string = "Kernel";
+ break;
+ case 'H':
+ string = "Hardware";
+ break;
+ default:
+ string = "Invalid";
+ break;
+ }
+ printf("%s", string);
+ break;
case 'P': /* frequency in ppm */
dbl = va_arg(ap, double);
if (sign)
@@ -1802,10 +1895,9 @@ print_report(const char *format, ...)
else
print_freq_ppm(dbl);
break;
- case 'R': /* reference ID in quad-dotted notation */
+ case 'R': /* reference ID in hexdecimal */
long_uinteger = va_arg(ap, unsigned long);
- printf("%lu.%lu.%lu.%lu", long_uinteger >> 24, (long_uinteger >> 16) & 0xff,
- (long_uinteger >> 8) & 0xff, long_uinteger & 0xff);
+ printf("%08lX", long_uinteger);
break;
case 'S': /* offset with unit */
dbl = va_arg(ap, double);
@@ -1814,9 +1906,9 @@ print_report(const char *format, ...)
else
print_nanoseconds(dbl);
break;
- case 'T': /* timeval as date and time in UTC */
- tv = va_arg(ap, struct timeval *);
- tm = gmtime(&tv->tv_sec);
+ case 'T': /* timespec as date and time in UTC */
+ ts = va_arg(ap, struct timespec *);
+ tm = gmtime(&ts->tv_sec);
if (!tm)
break;
strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
@@ -1826,9 +1918,14 @@ print_report(const char *format, ...)
long_uinteger = va_arg(ap, unsigned long);
printf("%*lu", width, long_uinteger);
break;
- case 'V': /* timeval as seconds since epoch */
- tv = va_arg(ap, struct timeval *);
- printf("%s", UTI_TimevalToString(tv));
+ case 'V': /* timespec as seconds since epoch */
+ ts = va_arg(ap, struct timespec *);
+ printf("%s", UTI_TimespecToString(ts));
+ break;
+ case 'b': /* unsigned int in binary */
+ uinteger = va_arg(ap, unsigned int);
+ for (i = prec - 1; i >= 0; i--)
+ printf("%c", uinteger & 1U << i ? '1' : '0');
break;
/* Classic printf specifiers */
@@ -1899,8 +1996,10 @@ format_name(char *buf, int size, int trunc_dns, int ref, uint32_t ref_id,
snprintf(buf, size, "%s", UTI_IPToString(ip_addr));
} else {
DNS_IPAddress2Name(ip_addr, buf, size);
- if (size > trunc_dns)
+ if (trunc_dns > 0 && strlen(buf) > trunc_dns) {
+ buf[trunc_dns - 1] = '>';
buf[trunc_dns] = '\0';
+ }
}
}
@@ -2067,7 +2166,7 @@ process_cmd_sourcestats(char *line)
format_name(name, sizeof (name), 25, ip_addr.family == IPADDR_UNSPEC,
ntohl(reply.data.sourcestats.ref_id), &ip_addr);
- print_report("%-25s %3u %3u %I %+P %P %+S %S\n",
+ print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
name,
(unsigned long)ntohl(reply.data.sourcestats.n_samples),
(unsigned long)ntohl(reply.data.sourcestats.n_runs),
@@ -2092,8 +2191,7 @@ process_cmd_tracking(char *line)
IPAddr ip_addr;
uint32_t ref_id;
char name[50];
- struct timeval ref_time;
- const char *leap_status;
+ struct timespec ref_time;
request.command = htons(REQ_TRACKING);
if (!request_reply(&request, &reply, RPY_TRACKING, 0))
@@ -2105,25 +2203,7 @@ process_cmd_tracking(char *line)
format_name(name, sizeof (name), sizeof (name),
ip_addr.family == IPADDR_UNSPEC, ref_id, &ip_addr);
- switch (ntohs(reply.data.tracking.leap_status)) {
- case LEAP_Normal:
- leap_status = "Normal";
- break;
- case LEAP_InsertSecond:
- leap_status = "Insert second";
- break;
- case LEAP_DeleteSecond:
- leap_status = "Delete second";
- break;
- case LEAP_Unsynchronised:
- leap_status = "Not synchronised";
- break;
- default:
- leap_status = "Unknown";
- break;
- }
-
- UTI_TimevalNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
+ UTI_TimespecNetworkToHost(&reply.data.tracking.ref_time, &ref_time);
print_report("Reference ID : %R (%s)\n"
"Stratum : %u\n"
@@ -2137,7 +2217,7 @@ process_cmd_tracking(char *line)
"Root delay : %.6f seconds\n"
"Root dispersion : %.6f seconds\n"
"Update interval : %.1f seconds\n"
- "Leap status : %s\n",
+ "Leap status : %L\n",
(unsigned long)ref_id, name,
ntohs(reply.data.tracking.stratum),
&ref_time,
@@ -2150,7 +2230,85 @@ process_cmd_tracking(char *line)
UTI_FloatNetworkToHost(reply.data.tracking.root_delay),
UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion),
UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval),
- leap_status, REPORT_END);
+ ntohs(reply.data.tracking.leap_status), REPORT_END);
+
+ return 1;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_ntpdata(char *line)
+{
+ CMD_Request request;
+ CMD_Reply reply;
+ IPAddr remote_addr, local_addr;
+ struct timespec ref_time;
+
+ if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
+ LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
+ return 0;
+ }
+
+ request.command = htons(REQ_NTP_DATA);
+ UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
+ if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
+ return 0;
+
+ UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
+ UTI_IPNetworkToHost(&reply.data.ntp_data.local_addr, &local_addr);
+ UTI_TimespecNetworkToHost(&reply.data.ntp_data.ref_time, &ref_time);
+
+ print_report("Remote address : %s (%R)\n"
+ "Remote port : %u\n"
+ "Local address : %s (%R)\n"
+ "Leap status : %L\n"
+ "Version : %u\n"
+ "Mode : %M\n"
+ "Stratum : %u\n"
+ "Poll : %d\n"
+ "Precision : %.9f seconds\n"
+ "Root delay : %.6f seconds\n"
+ "Root dispersion : %.6f seconds\n"
+ "Reference ID : %R\n"
+ "Reference time : %T\n"
+ "Offset : %+.9f seconds\n"
+ "Peer delay : %.9f seconds\n"
+ "Peer dispersion : %.9f seconds\n"
+ "Response time : %.9f seconds\n"
+ "Jitter asymmetry: %+.2f\n"
+ "NTP tests : %.3b %.3b %.4b\n"
+ "Interleaved : %B\n"
+ "Authenticated : %B\n"
+ "TX timestamping : %N\n"
+ "RX timestamping : %N\n"
+ "Total TX : %U\n"
+ "Total RX : %U\n"
+ "Total valid RX : %U\n",
+ UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
+ ntohs(reply.data.ntp_data.remote_port),
+ UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
+ reply.data.ntp_data.leap, reply.data.ntp_data.version,
+ reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
+ reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.precision),
+ UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
+ UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
+ (unsigned long)ntohl(reply.data.ntp_data.ref_id), &ref_time,
+ UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
+ UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay),
+ UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion),
+ UTI_FloatNetworkToHost(reply.data.ntp_data.response_time),
+ UTI_FloatNetworkToHost(reply.data.ntp_data.jitter_asymmetry),
+ ntohs(reply.data.ntp_data.flags) >> 7,
+ ntohs(reply.data.ntp_data.flags) >> 4,
+ ntohs(reply.data.ntp_data.flags),
+ ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
+ ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
+ reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
+ (unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
+ (unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
+ (unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
+ REPORT_END);
return 1;
}
@@ -2197,13 +2355,13 @@ process_cmd_smoothing(char *line)
flags = ntohl(reply.data.smoothing.flags);
- print_report("Active : %s %s\n"
+ print_report("Active : %B %s\n"
"Offset : %+.9f seconds\n"
"Frequency : %+.6f ppm\n"
"Wander : %+.6f ppm per second\n"
"Last update : %.1f seconds ago\n"
"Remaining time : %.1f seconds\n",
- flags & RPY_SMT_FLAG_ACTIVE ? "Yes" : "No",
+ !!(flags & RPY_SMT_FLAG_ACTIVE),
flags & RPY_SMT_FLAG_LEAPONLY ? "(leap second only)" : "",
UTI_FloatNetworkToHost(reply.data.smoothing.offset),
UTI_FloatNetworkToHost(reply.data.smoothing.freq_ppm),
@@ -2241,13 +2399,13 @@ process_cmd_rtcreport(char *line)
{
CMD_Request request;
CMD_Reply reply;
- struct timeval ref_time;
+ struct timespec ref_time;
request.command = htons(REQ_RTCREPORT);
if (!request_reply(&request, &reply, RPY_RTC, 0))
return 0;
- UTI_TimevalNetworkToHost(&reply.data.rtc.ref_time, &ref_time);
+ UTI_TimespecNetworkToHost(&reply.data.rtc.ref_time, &ref_time);
print_report("RTC ref time (UTC) : %T\n"
"Number of samples : %u\n"
@@ -2303,7 +2461,7 @@ process_cmd_clients(char *line)
if (ip.family == IPADDR_UNSPEC)
continue;
- format_name(name, sizeof (name), sizeof (name), 0, 0, &ip);
+ format_name(name, sizeof (name), 25, 0, 0, &ip);
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
name,
@@ -2339,7 +2497,7 @@ process_cmd_manual_list(const char *line)
CMD_Reply reply;
uint32_t i, n_samples;
RPY_ManualListSample *sample;
- struct timeval when;
+ struct timespec when;
request.command = htons(REQ_MANUAL_LIST);
if (!request_reply(&request, &reply, RPY_MANUAL_LIST, 0))
@@ -2352,7 +2510,7 @@ process_cmd_manual_list(const char *line)
for (i = 0; i < n_samples; i++) {
sample = &reply.data.manual_list.samples[i];
- UTI_TimevalNetworkToHost(&sample->when, &when);
+ UTI_TimespecNetworkToHost(&sample->when, &when);
print_report("%2d %s %10.2f %10.2f %10.2f\n",
i, UTI_TimeToLogForm(when.tv_sec),
@@ -2387,7 +2545,7 @@ process_cmd_manual_delete(CMD_Request *msg, const char *line)
static int
process_cmd_settime(char *line)
{
- struct timeval ts;
+ struct timespec ts;
time_t now, new_time;
CMD_Request request;
CMD_Reply reply;
@@ -2402,8 +2560,8 @@ process_cmd_settime(char *line)
printf("510 - Could not parse date string\n");
} else {
ts.tv_sec = new_time;
- ts.tv_usec = 0;
- UTI_TimevalHostToNetwork(&ts, &request.data.settime.ts);
+ ts.tv_nsec = 0;
+ UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts);
request.command = htons(REQ_SETTIME);
if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP, 1)) {
offset_cs = ntohl(reply.data.manual_timestamp.centiseconds);
@@ -2518,6 +2676,7 @@ process_cmd_waitsync(char *line)
{
CMD_Request request;
CMD_Reply reply;
+ IPAddr ip_addr;
uint32_t ref_id;
double correction, skew_ppm, max_correction, max_skew_ppm, interval;
int ret = 0, max_tries, i;
@@ -2539,6 +2698,7 @@ process_cmd_waitsync(char *line)
for (i = 1; ; i++) {
if (request_reply(&request, &reply, RPY_TRACKING, 0)) {
ref_id = ntohl(reply.data.tracking.ref_id);
+ UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr);
correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction);
correction = fabs(correction);
@@ -2547,7 +2707,8 @@ process_cmd_waitsync(char *line)
print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END);
- if (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */ &&
+ if ((ip_addr.family != IPADDR_UNSPEC ||
+ (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
(max_correction == 0.0 || correction <= max_correction) &&
(max_skew_ppm == 0.0 || skew_ppm <= max_skew_ppm)) {
ret = 1;
@@ -2770,6 +2931,9 @@ process_line(char *line)
do_normal_submit = process_cmd_minpoll(&tx_message, line);
} else if (!strcmp(command, "minstratum")) {
do_normal_submit = process_cmd_minstratum(&tx_message, line);
+ } else if (!strcmp(command, "ntpdata")) {
+ do_normal_submit = 0;
+ ret = process_cmd_ntpdata(line);
} else if (!strcmp(command, "offline")) {
do_normal_submit = process_cmd_offline(&tx_message, line);
} else if (!strcmp(command, "online")) {
diff --git a/clientlog.c b/clientlog.c
index 488340b..9dca9f1 100644
--- a/clientlog.c
+++ b/clientlog.c
@@ -39,6 +39,7 @@
#include "clientlog.h"
#include "conf.h"
#include "memory.h"
+#include "ntp.h"
#include "reports.h"
#include "util.h"
#include "logging.h"
@@ -57,6 +58,8 @@ typedef struct {
int8_t cmd_rate;
int8_t ntp_timeout_rate;
uint8_t flags;
+ NTP_int64 ntp_rx_ts;
+ NTP_int64 ntp_tx_ts;
} Record;
/* Hash table of records, there is a fixed number of records per slot */
@@ -155,7 +158,7 @@ get_record(IPAddr *ip)
time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record;
- if (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6)
+ if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
return NULL;
while (1) {
@@ -206,6 +209,8 @@ get_record(IPAddr *ip)
record->ntp_rate = record->cmd_rate = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE;
record->flags = 0;
+ UTI_ZeroNtp64(&record->ntp_rx_ts);
+ UTI_ZeroNtp64(&record->ntp_tx_ts);
return record;
}
@@ -336,23 +341,24 @@ CLG_Finalise(void)
/* ================================================== */
static uint32_t
-get_ts_from_timeval(struct timeval *tv)
+get_ts_from_timespec(struct timespec *ts)
{
- uint32_t sec = tv->tv_sec, usec = tv->tv_usec;
+ uint32_t sec = ts->tv_sec, nsec = ts->tv_nsec;
- return sec << TS_FRAC | (4295U * usec - (usec >> 5)) >> (32 - TS_FRAC);
+ /* This is fast and accurate enough */
+ return sec << TS_FRAC | (140740U * (nsec >> 15)) >> (32 - TS_FRAC);
}
/* ================================================== */
static void
-update_record(struct timeval *now, uint32_t *last_hit, uint32_t *hits,
+update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
{
uint32_t interval, now_ts, prev_hit, new_tokens;
int interval2;
- now_ts = get_ts_from_timeval(now);
+ now_ts = get_ts_from_timespec(now);
prev_hit = *last_hit;
*last_hit = now_ts;
@@ -405,15 +411,26 @@ get_index(Record *record)
/* ================================================== */
int
-CLG_LogNTPAccess(IPAddr *client, struct timeval *now)
+CLG_GetClientIndex(IPAddr *client)
{
Record *record;
- total_ntp_hits++;
-
- if (!active)
+ record = get_record(client);
+ if (record == NULL)
return -1;
+ return get_index(record);
+}
+
+/* ================================================== */
+
+int
+CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
+{
+ Record *record;
+
+ total_ntp_hits++;
+
record = get_record(client);
if (record == NULL)
return -1;
@@ -435,15 +452,12 @@ CLG_LogNTPAccess(IPAddr *client, struct timeval *now)
/* ================================================== */
int
-CLG_LogCommandAccess(IPAddr *client, struct timeval *now)
+CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
{
Record *record;
total_cmd_hits++;
- if (!active)
- return -1;
-
record = get_record(client);
if (record == NULL)
return -1;
@@ -552,7 +566,19 @@ CLG_LimitCommandResponseRate(int index)
/* ================================================== */
-extern int
+void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
+{
+ Record *record;
+
+ record = ARR_GetElement(records, index);
+
+ *rx_ts = &record->ntp_rx_ts;
+ *tx_ts = &record->ntp_tx_ts;
+}
+
+/* ================================================== */
+
+int
CLG_GetNumberOfIndices(void)
{
if (!active)
@@ -586,7 +612,7 @@ static uint32_t get_last_ago(uint32_t x, uint32_t y)
/* ================================================== */
int
-CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now)
+CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now)
{
Record *record;
uint32_t now_ts;
@@ -599,7 +625,7 @@ CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *repo
if (record->ip_addr.family == IPADDR_UNSPEC)
return 0;
- now_ts = get_ts_from_timeval(now);
+ now_ts = get_ts_from_timespec(now);
report->ip_addr = record->ip_addr;
report->ntp_hits = record->ntp_hits;
diff --git a/clientlog.h b/clientlog.h
index 6f77b35..337b4d8 100644
--- a/clientlog.h
+++ b/clientlog.h
@@ -33,15 +33,17 @@
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
-extern int CLG_LogNTPAccess(IPAddr *client, struct timeval *now);
-extern int CLG_LogCommandAccess(IPAddr *client, struct timeval *now);
+extern int CLG_GetClientIndex(IPAddr *client);
+extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
+extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
+extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
/* And some reporting functions, for use by chronyc. */
extern int CLG_GetNumberOfIndices(void);
-extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now);
+extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */
diff --git a/cmdmon.c b/cmdmon.c
index 26622d2..cbfcdea 100644
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -133,6 +133,9 @@ static const char permissions[] = {
PERMIT_AUTH, /* SERVER_STATS */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
PERMIT_AUTH, /* LOCAL2 */
+ PERMIT_AUTH, /* NTP_DATA */
+ PERMIT_AUTH, /* ADD_SERVER2 */
+ PERMIT_AUTH, /* ADD_PEER2 */
};
/* ================================================== */
@@ -143,7 +146,7 @@ static ADF_AuthTable access_auth_table;
/* ================================================== */
/* Forward prototypes */
-static void read_from_cmd_socket(void *anything);
+static void read_from_cmd_socket(int sock_fd, int event, void *anything);
/* ================================================== */
@@ -242,7 +245,7 @@ prepare_socket(int family, int port_number)
}
/* Register handler for read events on the socket */
- SCH_AddInputFileHandler(sock_fd, read_from_cmd_socket, (void *)(long)sock_fd);
+ SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_from_cmd_socket, NULL);
return sock_fd;
}
@@ -325,19 +328,19 @@ void
CAM_Finalise(void)
{
if (sock_fdu >= 0) {
- SCH_RemoveInputFileHandler(sock_fdu);
+ SCH_RemoveFileHandler(sock_fdu);
close(sock_fdu);
unlink(CNF_GetBindCommandPath());
}
sock_fdu = -1;
if (sock_fd4 >= 0) {
- SCH_RemoveInputFileHandler(sock_fd4);
+ SCH_RemoveFileHandler(sock_fd4);
close(sock_fd4);
}
sock_fd4 = -1;
#ifdef FEAT_IPV6
if (sock_fd6 >= 0) {
- SCH_RemoveInputFileHandler(sock_fd6);
+ SCH_RemoveFileHandler(sock_fd6);
close(sock_fd6);
}
sock_fd6 = -1;
@@ -564,10 +567,10 @@ handle_modify_makestep(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
{
- struct timeval ts;
+ struct timespec ts;
long offset_cs;
double dfreq_ppm, new_afreq_ppm;
- UTI_TimevalNetworkToHost(&rx_message->data.settime.ts, &ts);
+ UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts);
if (!MNL_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED);
} else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
@@ -634,7 +637,7 @@ static void
handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SourceReport report;
- struct timeval now_corr;
+ struct timespec now_corr;
/* Get data */
SCH_GetLastEventTime(&now_corr, NULL, NULL);
@@ -777,26 +780,29 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
+ params.min_stratum = ntohl(rx_message->data.ntp_source.min_stratum);
+ params.poll_target = ntohl(rx_message->data.ntp_source.poll_target);
+ params.version = ntohl(rx_message->data.ntp_source.version);
+ params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
+ params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
+ params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
+ params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
+ params.max_delay_ratio =
+ UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
+ params.max_delay_dev_ratio =
+ UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
+ params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
+
params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
+ params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
- params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
- params.max_delay_ratio = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
-
- /* not transmitted in cmdmon protocol yet */
- params.min_stratum = SRC_DEFAULT_MINSTRATUM;
- params.poll_target = SRC_DEFAULT_POLLTARGET;
- params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
- params.version = NTP_VERSION;
- params.max_sources = SRC_DEFAULT_MAXSOURCES;
- params.min_samples = SRC_DEFAULT_MINSAMPLES;
- params.max_samples = SRC_DEFAULT_MAXSAMPLES;
status = NSR_AddSource(&rem_addr, type, &params);
switch (status) {
@@ -898,7 +904,7 @@ handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPHostToNetwork(&rpt.ip_addr, &tx_message->data.tracking.ip_addr);
tx_message->data.tracking.stratum = htons(rpt.stratum);
tx_message->data.tracking.leap_status = htons(rpt.leap_status);
- UTI_TimevalHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time);
+ UTI_TimespecHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time);
tx_message->data.tracking.current_correction = UTI_FloatHostToNetwork(rpt.current_correction);
tx_message->data.tracking.last_offset = UTI_FloatHostToNetwork(rpt.last_offset);
tx_message->data.tracking.rms_offset = UTI_FloatHostToNetwork(rpt.rms_offset);
@@ -916,7 +922,7 @@ static void
handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SmoothingReport report;
- struct timeval now;
+ struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL);
@@ -940,7 +946,7 @@ handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_smoothtime(CMD_Request *rx_message, CMD_Reply *tx_message)
{
- struct timeval now;
+ struct timespec now;
int option;
if (!SMT_IsEnabled()) {
@@ -971,7 +977,7 @@ handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int status;
RPT_SourcestatsReport report;
- struct timeval now_corr;
+ struct timespec now_corr;
SCH_GetLastEventTime(&now_corr, NULL, NULL);
status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index),
@@ -1004,7 +1010,7 @@ handle_rtcreport(CMD_Request *rx_message, CMD_Reply *tx_message)
status = RTC_GetReport(&report);
if (status) {
tx_message->reply = htons(RPY_RTC);
- UTI_TimevalHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time);
+ UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time);
tx_message->data.rtc.n_samples = htons(report.n_samples);
tx_message->data.rtc.n_runs = htons(report.n_runs);
tx_message->data.rtc.span_seconds = htonl(report.span_seconds);
@@ -1041,7 +1047,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
RPY_ClientAccesses_Client *client;
int n_indices;
uint32_t i, j, req_first_index, req_n_clients;
- struct timeval now;
+ struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1100,7 +1106,7 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.manual_list.n_samples = htonl(n_samples);
for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i];
- UTI_TimevalHostToNetwork(&report[i].when, &sample->when);
+ UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
sample->slewed_offset = UTI_FloatHostToNetwork(report[i].slewed_offset);
sample->orig_offset = UTI_FloatHostToNetwork(report[i].orig_offset);
sample->residual = UTI_FloatHostToNetwork(report[i].residual);
@@ -1186,25 +1192,67 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
}
/* ================================================== */
+
+static void
+handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ RPT_NTPReport report;
+
+ UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &report.remote_addr);
+
+ if (!NSR_GetNTPReport(&report)) {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ return;
+ }
+
+ tx_message->reply = htons(RPY_NTP_DATA);
+ UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
+ UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
+ tx_message->data.ntp_data.remote_port = htons(report.remote_port);
+ tx_message->data.ntp_data.leap = report.leap;
+ tx_message->data.ntp_data.version = report.version;
+ tx_message->data.ntp_data.mode = report.mode;
+ tx_message->data.ntp_data.stratum = report.stratum;
+ tx_message->data.ntp_data.poll = report.poll;
+ tx_message->data.ntp_data.precision = report.precision;
+ tx_message->data.ntp_data.root_delay = UTI_FloatHostToNetwork(report.root_delay);
+ tx_message->data.ntp_data.root_dispersion = UTI_FloatHostToNetwork(report.root_dispersion);
+ tx_message->data.ntp_data.ref_id = htonl(report.ref_id);
+ UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.ntp_data.ref_time);
+ tx_message->data.ntp_data.offset = UTI_FloatHostToNetwork(report.offset);
+ tx_message->data.ntp_data.peer_delay = UTI_FloatHostToNetwork(report.peer_delay);
+ tx_message->data.ntp_data.peer_dispersion = UTI_FloatHostToNetwork(report.peer_dispersion);
+ tx_message->data.ntp_data.response_time = UTI_FloatHostToNetwork(report.response_time);
+ tx_message->data.ntp_data.jitter_asymmetry = UTI_FloatHostToNetwork(report.jitter_asymmetry);
+ tx_message->data.ntp_data.flags = htons((report.tests & RPY_NTP_FLAGS_TESTS) |
+ (report.interleaved ? RPY_NTP_FLAG_INTERLEAVED : 0) |
+ (report.authenticated ? RPY_NTP_FLAG_AUTHENTICATED : 0));
+ tx_message->data.ntp_data.tx_tss_char = report.tx_tss_char;
+ tx_message->data.ntp_data.rx_tss_char = report.rx_tss_char;
+ tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
+ tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
+ tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
+}
+
+/* ================================================== */
/* Read a packet and process it */
static void
-read_from_cmd_socket(void *anything)
+read_from_cmd_socket(int sock_fd, int event, void *anything)
{
CMD_Request rx_message;
CMD_Reply tx_message;
int status, read_length, expected_length, rx_message_length;
- int localhost, allowed, sock_fd, log_index;
+ int localhost, allowed, log_index;
union sockaddr_all where_from;
socklen_t from_length;
IPAddr remote_ip;
unsigned short remote_port, rx_command;
- struct timeval now, cooked_now;
+ struct timespec now, cooked_now;
rx_message_length = sizeof(rx_message);
from_length = sizeof(where_from);
- sock_fd = (long)anything;
status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0,
&where_from.sa, &from_length);
@@ -1477,11 +1525,11 @@ read_from_cmd_socket(void *anything)
handle_cmdaccheck(&rx_message, &tx_message);
break;
- case REQ_ADD_SERVER:
+ case REQ_ADD_SERVER2:
handle_add_source(NTP_SERVER, &rx_message, &tx_message);
break;
- case REQ_ADD_PEER:
+ case REQ_ADD_PEER2:
handle_add_source(NTP_PEER, &rx_message, &tx_message);
break;
@@ -1573,6 +1621,10 @@ read_from_cmd_socket(void *anything)
handle_server_stats(&rx_message, &tx_message);
break;
+ case REQ_NTP_DATA:
+ handle_ntp_data(&rx_message, &tx_message);
+ break;
+
default:
DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
diff --git a/cmdparse.c b/cmdparse.c
index b3f14de..700da32 100644
--- a/cmdparse.c
+++ b/cmdparse.c
@@ -39,186 +39,115 @@
/* ================================================== */
-CPS_Status
+int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
- int n, done;
- CPS_Status result;
+ int n;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
- src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
- src->params.authkey = INACTIVE_AUTHKEY;
- src->params.max_delay = SRC_DEFAULT_MAXDELAY;
- src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
- src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.online = 1;
src->params.auto_offline = 0;
+ src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
- src->params.version = NTP_VERSION;
+ src->params.version = 0;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
+ src->params.interleaved = 0;
src->params.sel_options = 0;
+ src->params.authkey = INACTIVE_AUTHKEY;
+ src->params.max_delay = SRC_DEFAULT_MAXDELAY;
+ src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
+ src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
+ src->params.offset = 0.0;
- result = CPS_Success;
-
hostname = line;
line = CPS_SplitWord(line);
- if (!*hostname) {
- result = CPS_BadHost;
- } else {
- src->name = hostname;
-
- /* Parse subfields */
- done = 0;
- do {
- cmd = line;
- line = CPS_SplitWord(line);
-
- if (*cmd) {
- if (!strcasecmp(cmd, "port")) {
- if (sscanf(line, "%hu%n", &src->port, &n) != 1) {
- result = CPS_BadPort;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "minpoll")) {
- if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) {
- result = CPS_BadMinpoll;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "maxpoll")) {
- if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) {
- result = CPS_BadMaxpoll;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "presend")) {
- if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) {
- result = CPS_BadPresend;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "maxdelaydevratio")) {
- if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) {
- result = CPS_BadMaxdelaydevratio;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "maxdelayratio")) {
- if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) {
- result = CPS_BadMaxdelayratio;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "maxdelay")) {
- if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) {
- result = CPS_BadMaxdelay;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "key")) {
- if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
- src->params.authkey == INACTIVE_AUTHKEY) {
- result = CPS_BadKey;
- done = 1;
- } else {
- line += n;
- }
- } else if (!strcasecmp(cmd, "offline")) {
- src->params.online = 0;
-
- } else if (!strcasecmp(cmd, "auto_offline")) {
- src->params.auto_offline = 1;
-
- } else if (!strcasecmp(cmd, "iburst")) {
- src->params.iburst = 1;
-
- } else if (!strcasecmp(cmd, "minstratum")) {
- if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) {
- result = CPS_BadMinstratum;
- done = 1;
- } else {
- line += n;
- }
-
- } else if (!strcasecmp(cmd, "polltarget")) {
- if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) {
- result = CPS_BadPolltarget;
- done = 1;
- } else {
- line += n;
- }
-
- } else if (!strcasecmp(cmd, "noselect")) {
- src->params.sel_options |= SRC_SELECT_NOSELECT;
-
- } else if (!strcasecmp(cmd, "prefer")) {
- src->params.sel_options |= SRC_SELECT_PREFER;
-
- } else if (!strcasecmp(cmd, "trust")) {
- src->params.sel_options |= SRC_SELECT_TRUST;
-
- } else if (!strcasecmp(cmd, "require")) {
- src->params.sel_options |= SRC_SELECT_REQUIRE;
-
- } else if (!strcasecmp(cmd, "version")) {
- if (sscanf(line, "%d%n", &src->params.version, &n) != 1) {
- result = CPS_BadVersion;
- done = 1;
- } else {
- line += n;
- }
-
- } else if (!strcasecmp(cmd, "maxsources")) {
- if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) {
- result = CPS_BadMaxsources;
- done = 1;
- } else {
- line += n;
- }
-
- } else if (!strcasecmp(cmd, "minsamples")) {
- if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1) {
- result = CPS_BadMinsamples;
- done = 1;
- } else {
- line += n;
- }
-
- } else if (!strcasecmp(cmd, "maxsamples")) {
- if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1) {
- result = CPS_BadMaxsamples;
- done = 1;
- } else {
- line += n;
- }
-
- } else {
- result = CPS_BadOption;
- done = 1;
- }
- } else {
- done = 1;
- }
- } while (!done);
+ if (!*hostname)
+ return 0;
+
+ src->name = hostname;
+
+ /* Parse options */
+ for (; *line; line += n) {
+ cmd = line;
+ line = CPS_SplitWord(line);
+ n = 0;
+
+ if (!strcasecmp(cmd, "auto_offline")) {
+ src->params.auto_offline = 1;
+ } else if (!strcasecmp(cmd, "iburst")) {
+ src->params.iburst = 1;
+ } else if (!strcasecmp(cmd, "offline")) {
+ src->params.online = 0;
+ } else if (!strcasecmp(cmd, "noselect")) {
+ src->params.sel_options |= SRC_SELECT_NOSELECT;
+ } else if (!strcasecmp(cmd, "prefer")) {
+ src->params.sel_options |= SRC_SELECT_PREFER;
+ } else if (!strcasecmp(cmd, "require")) {
+ src->params.sel_options |= SRC_SELECT_REQUIRE;
+ } else if (!strcasecmp(cmd, "trust")) {
+ src->params.sel_options |= SRC_SELECT_TRUST;
+ } else if (!strcasecmp(cmd, "key")) {
+ if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
+ src->params.authkey == INACTIVE_AUTHKEY)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxdelay")) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxdelayratio")) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxdelaydevratio")) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxpoll")) {
+ if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxsamples")) {
+ if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "maxsources")) {
+ if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "minpoll")) {
+ if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "minsamples")) {
+ if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "minstratum")) {
+ if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "offset")) {
+ if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "port")) {
+ if (sscanf(line, "%hu%n", &src->port, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "polltarget")) {
+ if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "presend")) {
+ if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "version")) {
+ if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "xleave")) {
+ src->params.interleaved = 1;
+ } else {
+ return 0;
+ }
}
- return result;
+ return 1;
}
/* ================================================== */
@@ -256,71 +185,6 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
return 1;
}
-/* ================================================== */
-
-void
-CPS_StatusToString(CPS_Status status, char *dest, int len)
-{
- const char *s = NULL;
-
- if (len > 0)
- dest[0] = '\0';
-
- switch (status) {
- case CPS_Success:
- return;
- case CPS_BadOption:
- s = "server/peer/pool option";
- break;
- case CPS_BadHost:
- s = "address";
- break;
- case CPS_BadPort:
- s = "port";
- break;
- case CPS_BadMinpoll:
- s = "minpoll";
- break;
- case CPS_BadMaxpoll:
- s = "maxpoll";
- break;
- case CPS_BadPresend:
- s = "presend";
- break;
- case CPS_BadMaxdelaydevratio:
- s = "maxdelaydevratio";
- break;
- case CPS_BadMaxdelayratio:
- s = "maxdelayratio";
- break;
- case CPS_BadMaxdelay:
- s = "maxdelay";
- break;
- case CPS_BadKey:
- s = "key";
- break;
- case CPS_BadMinstratum:
- s = "minstratum";
- break;
- case CPS_BadPolltarget:
- s = "polltarget";
- break;
- case CPS_BadVersion:
- s = "version";
- break;
- case CPS_BadMaxsources:
- s = "maxsources";
- break;
- case CPS_BadMinsamples:
- s = "minsamples";
- break;
- case CPS_BadMaxsamples:
- s = "maxsamples";
- break;
- }
-
- snprintf(dest, len, "Invalid %s", s);
-}
/* ================================================== */
diff --git a/cmdparse.h b/cmdparse.h
index a61fdc2..19f4bb7 100644
--- a/cmdparse.h
+++ b/cmdparse.h
@@ -30,26 +30,6 @@
#include "srcparams.h"
#include "addressing.h"
-typedef enum {
- CPS_Success,
- CPS_BadOption,
- CPS_BadHost,
- CPS_BadPort,
- CPS_BadMinpoll,
- CPS_BadMaxpoll,
- CPS_BadPresend,
- CPS_BadMaxdelaydevratio,
- CPS_BadMaxdelayratio,
- CPS_BadMaxdelay,
- CPS_BadKey,
- CPS_BadMinstratum,
- CPS_BadPolltarget,
- CPS_BadVersion,
- CPS_BadMaxsources,
- CPS_BadMinsamples,
- CPS_BadMaxsamples,
-} CPS_Status;
-
typedef struct {
char *name;
unsigned short port;
@@ -57,14 +37,11 @@ typedef struct {
} CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */
-extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
+extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
-/* Get a string describing error status */
-extern void CPS_StatusToString(CPS_Status status, char *dest, int len);
-
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);
diff --git a/conf.c b/conf.c
index 8470b7e..7500fd9 100644
--- a/conf.c
+++ b/conf.c
@@ -57,6 +57,7 @@ static void parse_bindcmdaddress(char *);
static void parse_broadcast(char *);
static void parse_clientloglimit(char *);
static void parse_fallbackdrift(char *);
+static void parse_hwtimestamp(char *);
static void parse_include(char *);
static void parse_initstepslew(char *);
static void parse_leapsecmode(char *);
@@ -89,6 +90,7 @@ static double max_drift = 500000.0; /* in ppm */
static double max_slew_rate = 1e6 / 12.0; /* in ppm */
static double max_distance = 3.0;
+static double max_jitter = 1.0;
static double reselect_distance = 1e-4;
static double stratum_weight = 1e-3;
static double combine_limit = 3.0;
@@ -147,7 +149,7 @@ static double max_offset;
/* Maximum and minimum number of samples per source */
static int max_samples = 0; /* no limit */
-static int min_samples = 0;
+static int min_samples = 6;
/* Threshold for a time adjustment to be logged to syslog */
static double log_change_threshold = 1.0;
@@ -181,6 +183,9 @@ static IPAddr bind_cmd_address4, bind_cmd_address6;
/* Path to the Unix domain command socket. */
static char *bind_cmd_path;
+/* Path to Samba (ntp_signd) socket. */
+static char *ntp_signd_socket = NULL;
+
/* Filename to use for storing pid of running chronyd, to prevent multiple
* chronyds being started. */
static char *pidfile;
@@ -218,6 +223,9 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
+/* Array of strings for interfaces with HW timestamping */
+static ARR_Instance hwts_interfaces;
+
typedef struct {
NTP_Source_Type type;
int pool;
@@ -319,6 +327,8 @@ CNF_Initialise(int r)
{
restarted = r;
+ hwts_interfaces = ARR_CreateInstance(sizeof (char *));
+
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
@@ -327,11 +337,11 @@ CNF_Initialise(int r)
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
- dumpdir = Strdup(".");
- logdir = Strdup(".");
+ dumpdir = Strdup("");
+ logdir = Strdup("");
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
- pidfile = Strdup("/var/run/chronyd.pid");
- rtc_device = Strdup("/dev/rtc");
+ pidfile = Strdup(DEFAULT_PID_FILE);
+ rtc_device = Strdup(DEFAULT_RTC_DEVICE);
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER);
}
@@ -343,6 +353,10 @@ CNF_Finalise(void)
{
unsigned int i;
+ for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
+ Free(*(char **)ARR_GetElement(hwts_interfaces, i));
+ ARR_DestroyInstance(hwts_interfaces);
+
for (i = 0; i < ARR_GetSize(ntp_sources); i++)
Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name);
@@ -361,6 +375,7 @@ CNF_Finalise(void)
Free(leapsec_tz);
Free(logdir);
Free(bind_cmd_path);
+ Free(ntp_signd_socket);
Free(pidfile);
Free(rtc_device);
Free(rtc_file);
@@ -458,6 +473,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_fallbackdrift(p);
} else if (!strcasecmp(command, "hwclockfile")) {
parse_string(p, &hwclock_file);
+ } else if (!strcasecmp(command, "hwtimestamp")) {
+ parse_hwtimestamp(p);
} else if (!strcasecmp(command, "include")) {
parse_include(p);
} else if (!strcasecmp(command, "initstepslew")) {
@@ -494,6 +511,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_double(p, &max_distance);
} else if (!strcasecmp(command, "maxdrift")) {
parse_double(p, &max_drift);
+ } else if (!strcasecmp(command, "maxjitter")) {
+ parse_double(p, &max_jitter);
} else if (!strcasecmp(command, "maxsamples")) {
parse_int(p, &max_samples);
} else if (!strcasecmp(command, "maxslewrate")) {
@@ -506,6 +525,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_int(p, &min_sources);
} else if (!strcasecmp(command, "noclientlog")) {
no_client_log = parse_null(p);
+ } else if (!strcasecmp(command, "ntpsigndsocket")) {
+ parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "peer")) {
parse_source(p, NTP_PEER, 0);
} else if (!strcasecmp(command, "pidfile")) {
@@ -604,17 +625,13 @@ parse_null(char *line)
static void
parse_source(char *line, NTP_Source_Type type, int pool)
{
- CPS_Status status;
NTP_Source source;
- char str[64];
source.type = type;
source.pool = pool;
- status = CPS_ParseNTPSourceAdd(line, &source.params);
- if (status != CPS_Success) {
- CPS_StatusToString(status, str, sizeof (str));
- other_parse_error(str);
+ if (!CPS_ParseNTPSourceAdd(line, &source.params)) {
+ command_parse_error();
return;
}
@@ -657,6 +674,7 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
+ int max_lock_age;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion;
char *p, *cmd, *name, *param;
@@ -675,6 +693,7 @@ parse_refclock(char *line)
precision = 0.0;
max_dispersion = 0.0;
ref_id = 0;
+ max_lock_age = 2;
lock_ref_id = 0;
if (!*line) {
@@ -725,6 +744,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &min_samples, &n) != 1)
break;
+ } else if (!strcasecmp(cmd, "maxlockage")) {
+ if (sscanf(line, "%d%n", &max_lock_age, &n) != 1)
+ break;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &max_samples, &n) != 1)
break;
@@ -778,6 +800,7 @@ parse_refclock(char *line)
refclock->precision = precision;
refclock->max_dispersion = max_dispersion;
refclock->ref_id = ref_id;
+ refclock->max_lock_age = max_lock_age;
refclock->lock_ref_id = lock_ref_id;
}
@@ -1220,6 +1243,15 @@ parse_tempcomp(char *line)
/* ================================================== */
static void
+parse_hwtimestamp(char *line)
+{
+ check_number_of_args(line, 1);
+ *(char **)ARR_GetNewElement(hwts_interfaces) = Strdup(line);
+}
+
+/* ================================================== */
+
+static void
parse_include(char *line)
{
glob_t gl;
@@ -1245,9 +1277,6 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
{
char *dir;
- UTI_CreateDirAndParents(logdir, 0755, uid, gid);
- UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
-
/* Create a directory for the Unix domain command socket */
if (bind_cmd_path[0]) {
dir = UTI_PathToDir(bind_cmd_path);
@@ -1263,6 +1292,11 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
Free(dir);
}
+
+ if (logdir[0])
+ UTI_CreateDirAndParents(logdir, 0755, uid, gid);
+ if (dumpdir[0])
+ UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
}
/* ================================================== */
@@ -1527,6 +1561,14 @@ CNF_GetMaxDistance(void)
/* ================================================== */
double
+CNF_GetMaxJitter(void)
+{
+ return max_jitter;
+}
+
+/* ================================================== */
+
+double
CNF_GetReselectDistance(void)
{
return reselect_distance;
@@ -1741,6 +1783,14 @@ CNF_GetBindCommandAddress(int family, IPAddr *addr)
/* ================================================== */
char *
+CNF_GetNtpSigndSocket(void)
+{
+ return ntp_signd_socket;
+}
+
+/* ================================================== */
+
+char *
CNF_GetPidFile(void)
{
return pidfile;
@@ -1877,3 +1927,11 @@ CNF_GetInitStepThreshold(void)
{
return init_slew_threshold;
}
+
+/* ================================================== */
+
+ARR_Instance
+CNF_GetHwTsInterfaces(void)
+{
+ return hwts_interfaces;
+}
diff --git a/conf.h b/conf.h
index 7168fb3..d241665 100644
--- a/conf.h
+++ b/conf.h
@@ -29,6 +29,7 @@
#define GOT_CONF_H
#include "addressing.h"
+#include "array.h"
#include "reference.h"
extern void CNF_Initialise(int restarted);
@@ -76,6 +77,7 @@ extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindCommandPath(void);
+extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
@@ -88,6 +90,7 @@ extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetMaxDistance(void);
+extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
@@ -117,4 +120,6 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
+extern ARR_Instance CNF_GetHwTsInterfaces(void);
+
#endif /* GOT_CONF_H */
diff --git a/configure b/configure
index cb1d139..6dd95c7 100755
--- a/configure
+++ b/configure
@@ -24,6 +24,7 @@ test_code () {
printf "%s" "Checking for $name : "
(
+ echo "#include \"config.h\""
for h in $headers; do
echo "#include <$h>"
done
@@ -79,9 +80,8 @@ For better control, use the options below.
--disable-readline Disable line editing support
--without-readline Don't use GNU readline even if it is available
--without-editline Don't use editline even if it is available
- --readline-dir=DIR Specify parent of readline include and lib directories
- --readline-inc-dir=DIR Specify where readline include directory is
- --readline-lib-dir=DIR Specify where readline lib directory is
+ --with-readline-includes=DIR Specify where readline include directory is
+ --with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5
--without-nss Don't use NSS even if it is available
@@ -99,10 +99,15 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
+ --without-clock-gettime Don't use clock_gettime() even if it is available
+ --disable-timestamping Disable support for SW/HW timestamping
+ --enable-ntp-signd Enable support for MS-SNTP authentication in Samba
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
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-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
@@ -114,7 +119,7 @@ Fine tuning of the installation directories:
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/chrony]
--localstatedir=DIR modifiable single-machine data [/var]
- --chronysockdir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony]
+ --chronyrundir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony]
--chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony]
Overriding system detection when cross-compiling:
@@ -213,9 +218,15 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
+try_clock_gettime=1
+feat_timestamping=1
+try_timestamping=0
+feat_ntp_signd=0
ntp_era_split=""
default_user="root"
default_hwclockfile=""
+default_pidfile="/var/run/chronyd.pid"
+default_rtcdevice="/dev/rtc"
mail_program="/usr/lib/sendmail"
for option
@@ -269,8 +280,8 @@ do
--localstatedir=* )
SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'`
;;
- --chronysockdir=* )
- SETCHRONYSOCKDIR=`echo $option | sed -e 's/^.*=//;'`
+ --chronyrundir=* | --chronysockdir=* )
+ SETCHRONYRUNDIR=`echo $option | sed -e 's/^.*=//;'`
;;
--chronyvardir=* )
SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'`
@@ -317,6 +328,15 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
+ --without-clock-gettime)
+ try_clock_gettime=0
+ ;;
+ --disable-timestamping)
+ feat_timestamping=0
+ ;;
+ --enable-ntp-signd)
+ feat_ntp_signd=1
+ ;;
--with-ntp-era=* )
ntp_era_split=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -326,6 +346,12 @@ do
--with-hwclockfile=* )
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
;;
+ --with-pidfile=* )
+ default_pidfile=`echo $option | sed -e 's/^.*=//;'`
+ ;;
+ --with-rtcdevice=* )
+ default_rtcdevice=`echo $option | sed -e 's/^.*=//;'`
+ ;;
--with-sendmail=* )
mail_program=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -366,6 +392,7 @@ case $OPERATINGSYSTEM in
[ $try_libcap != "0" ] && try_libcap=1
try_rtc=1
[ $try_seccomp != "0" ] && try_seccomp=1
+ try_timestamping=1
try_setsched=1
try_lockmem=1
try_phc=1
@@ -396,7 +423,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
fi
- echo "Configuring for MacOS X (" $SYSTEM "MacOS X version" $VERSION ")"
+ echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
@@ -432,8 +459,13 @@ fi
if [ $feat_ntp = "1" ]; then
add_def FEAT_NTP
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o"
+ if [ $feat_ntp_signd = "1" ]; then
+ add_def FEAT_SIGND
+ EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
+ fi
else
feat_asyncdns=0
+ feat_timestamping=0
fi
if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then
@@ -559,6 +591,21 @@ then
fi
fi
+if [ $try_clock_gettime = "1" ]; then
+ if test_code 'clock_gettime()' 'time.h' '' '' \
+ 'clock_gettime(CLOCK_REALTIME, NULL);'
+ then
+ add_def HAVE_CLOCK_GETTIME
+ else
+ if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
+ 'clock_gettime(CLOCK_REALTIME, NULL);'
+ then
+ add_def HAVE_CLOCK_GETTIME
+ EXTRA_LIBS="$EXTRA_LIBS -lrt"
+ fi
+ fi
+fi
+
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
'return getaddrinfo(0, 0, 0, 0);'
then
@@ -579,6 +626,33 @@ if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; the
add_def HAVE_ARC4RANDOM
fi
+RECVMMSG_CODE='
+ struct mmsghdr hdr;
+ return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
+if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
+ add_def HAVE_RECVMMSG
+else
+ if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
+ "$EXTRA_LIBS" "$RECVMMSG_CODE"
+ then
+ add_def _GNU_SOURCE
+ add_def HAVE_RECVMMSG
+ fi
+fi
+
+if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
+ test_code 'SW/HW timestamping' 'sys/types.h sys/socket.h linux/net_tstamp.h
+ linux/errqueue.h linux/ptp_clock.h' '' '' '
+ int val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG;
+ return sizeof (struct scm_timestamping) + SCM_TSTAMP_SND + PTP_SYS_OFFSET +
+ setsockopt(0, SOL_SOCKET, SO_SELECT_ERR_QUEUE + SO_TIMESTAMPING,
+ &val, sizeof (val));'
+then
+ add_def HAVE_LINUX_TIMESTAMPING
+ EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o"
+fi
+
timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
@@ -649,19 +723,11 @@ then
fi
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
+ grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
then
- if test_code 'clock_gettime()' 'time.h' '' '' 'clock_gettime(0, NULL);'; then
- add_def FEAT_PHC
- else
- if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
- 'clock_gettime(0, NULL);'
- then
- EXTRA_LIBS="$EXTRA_LIBS -lrt"
- add_def FEAT_PHC
- fi
- fi
+ add_def FEAT_PHC
fi
if [ $try_setsched = "1" ] && \
@@ -691,7 +757,6 @@ then
add_def FORCE_DNSRETRY
fi
-READLINE_COMPILE=""
READLINE_LINK=""
if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then
@@ -701,7 +766,7 @@ if [ $feat_readline = "1" ]; then
then
add_def FEAT_READLINE
add_def USE_EDITLINE
- READLINE_COMPILE="$readline_inc"
+ MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -ledit"
fi
fi
@@ -712,7 +777,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
- READLINE_COMPILE="$readline_inc"
+ MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -lreadline"
fi
fi
@@ -724,7 +789,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
- READLINE_COMPILE="$readline_inc"
+ MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi
fi
@@ -733,7 +798,6 @@ if [ $feat_readline = "1" ]; then
fi
HASH_OBJ="hash_intmd5.o"
-HASH_COMPILE=""
HASH_LINK=""
if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
@@ -744,9 +808,9 @@ if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
then
HASH_OBJ="hash_nss.o"
- HASH_COMPILE="$test_cflags"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
+ MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
fi
fi
@@ -756,9 +820,9 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
then
HASH_OBJ="hash_tomcrypt.o"
- HASH_COMPILE="-I/usr/include/tomcrypt"
HASH_LINK="-ltomcrypt"
LIBS="$LIBS $HASH_LINK"
+ MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/tomcrypt"
add_def FEAT_SECHASH
fi
fi
@@ -808,9 +872,9 @@ if [ "x$SETLOCALSTATEDIR" != "x" ]; then
LOCALSTATEDIR=$SETLOCALSTATEDIR
fi
-CHRONYSOCKDIR=${LOCALSTATEDIR}/run/chrony
-if [ "x$SETCHRONYSOCKDIR" != "x" ]; then
- CHRONYSOCKDIR=$SETCHRONYSOCKDIR
+CHRONYRUNDIR=${LOCALSTATEDIR}/run/chrony
+if [ "x$SETCHRONYRUNDIR" != "x" ]; then
+ CHRONYRUNDIR=$SETCHRONYRUNDIR
fi
CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony
@@ -820,13 +884,15 @@ fi
add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\""
add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
+add_def DEFAULT_PID_FILE "\"$default_pidfile\""
+add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
add_def DEFAULT_USER "\"$default_user\""
-add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYSOCKDIR/chronyd.sock\""
+add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
-chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH ASYNCDNS`"
+chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH SIGND ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
@@ -850,18 +916,18 @@ do
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
- s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
s%@HASH_OBJ@%${HASH_OBJ}%;\
- s%@HASH_COMPILE@%${HASH_COMPILE}%;\
s%@SYSCONFDIR@%${SYSCONFDIR}%;\
s%@BINDIR@%${BINDIR}%;\
s%@SBINDIR@%${SBINDIR}%;\
s%@DOCDIR@%${DOCDIR}%;\
s%@MANDIR@%${MANDIR}%;\
s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\
- s%@CHRONYSOCKDIR@%${CHRONYSOCKDIR}%;\
+ s%@CHRONYRUNDIR@%${CHRONYRUNDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\
+ s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
+ s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
s%@DEFAULT_USER@%${default_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f
diff --git a/contrib/bryan_christianson_1/README.txt b/contrib/bryan_christianson_1/README.txt
index 0a221aa..3a0a2ef 100644
--- a/contrib/bryan_christianson_1/README.txt
+++ b/contrib/bryan_christianson_1/README.txt
@@ -1,16 +1,16 @@
-Notes for installing chrony on MacOS X
+Notes for installing chrony on macOS
Author: Bryan Christianson (bryan@whatroute.net)
------------------------------------------------
These files are for those admins/users who would prefer to install chrony
from the source distribution and are intended as guidelines rather than
being definitive. They can be edited with a plain text editor, such as
-vi, emacs or your favourite IDE (xcode)
+vi, emacs or your favourite IDE (Xcode)
It is assumed you are comfortable with installing software from the
terminal command line and know how to use sudo to acquire root access.
-If you are not familiar with the MacOS X command line then
+If you are not familiar with the macOS command line then
please consider using ChronyControl from http://whatroute.net/chronycontrol.html
ChronyControl provides a gui wrapper for installing these files and sets the
@@ -72,7 +72,7 @@ Installing the support files
1. chronylogrotate.sh
This is a simple shell script that deletes old log files. Unfortunately because
-of the need to run chronyc, the standard MacOS X logrotation does not work with
+of the need to run chronyc, the standard macOS logrotation does not work with
chrony logs.
This script runs on a daily basis under control of launchd and should be
diff --git a/doc/Makefile.in b/doc/Makefile.in
index bd405df..1777da5 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -13,19 +13,23 @@ BINDIR = @BINDIR@
SBINDIR = @SBINDIR@
MANDIR = @MANDIR@
DOCDIR = @DOCDIR@
-CHRONYSOCKDIR = @CHRONYSOCKDIR@
+CHRONYRUNDIR = @CHRONYRUNDIR@
CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
+DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
+DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
s%\@BINDIR\@%$(BINDIR)%g;\
s%\@SBINDIR\@%$(SBINDIR)%g;\
s%\@CHRONY_VERSION\@%$(CHRONY_VERSION)%g;\
s%\@DEFAULT_HWCLOCK_FILE\@%$(DEFAULT_HWCLOCK_FILE)%g;\
+ s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
+ s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
- s%\@CHRONYSOCKDIR\@%$(CHRONYSOCKDIR)%g;\
+ s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
man: $(MAN_FILES) $(MAN_IN_FILES)
diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc
index 5fb68a3..6e9ab90 100644
--- a/doc/chrony.conf.adoc
+++ b/doc/chrony.conf.adoc
@@ -116,6 +116,13 @@ If a measurement has a ratio of the increase in the round-trip delay from the
minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0.
+*offset* _offset_:::
+This option specifies a correction (in seconds) which will be applied to
+offsets measured with this source. It's particularly useful to compensate for a
+known asymmetry in network delay or timestamping errors. For example, if
+packets sent to the source were on average delayed by 100 microseconds more
+than packets sent from the source back, the correction would be -0.00005 (-50
+microseconds). The default is 0.0.
*minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
@@ -149,11 +156,29 @@ clock. Together with the *trust* option this might be useful to allow a trusted
authenticated source to be safely combined with unauthenticated sources in
order to improve the accuracy of the clock. They can be selected and used for
synchronisation only if they agree with the trusted and required source.
+*xleave*:::
+This option enables an interleaved mode which allows the server or the peer to
+send transmit timestamps captured after the actual transmission (e.g. when the
+server or the peer is running *chronyd* with HW timestamping enabled by the
+<<hwtimestamp,*hwtimestamp*>> directive). This can significantly improve the
+accuracy of the measurements.
++
+The interleaved mode is compatible with servers that support only the basic
+mode, but peers must both support and have enabled the interleaved mode,
+otherwise the synchronisation will work only in one direction. Note that even
+servers that support the interleaved mode might respond in the basic mode as
+the interleaved mode requires the servers to keep some state for each client
+and the state might be dropped when there are too many clients (e.g.
+<<clientloglimit,*clientloglimit*>> is too small), or it might be overwritten
+by other clients that have the same IP address (e.g. computers behind NAT or
+someone sending requests with a spoofed source address). The *presend* option
+can be used to shorten the interval in which the server has to keep the state
+for this computer and be able to respond in the interleaved mode.
*polltarget* _target_:::
Target number of measurements to use for the regression algorithm which
*chronyd* will try to maintain by adjusting the polling interval between
*minpoll* and *maxpoll*. A higher target makes *chronyd* prefer shorter polling
-intervals. The default is 6 and a useful range is from 6 to 60.
+intervals. The default is 8 and a useful range is from 6 to 60.
*port* _port_:::
This option allows the UDP port on which the server understands NTP requests to
be specified. For normal servers this option should not be required (the
@@ -177,8 +202,11 @@ presend 9
----
+
when the polling interval is 512 seconds or more, an extra NTP client packet
-will be sent to the server a short time (4 seconds) before making the actual
+will be sent to the server a short time (2 seconds) before making the actual
measurement.
++
+The *presend* option cannot be used in the *peer* directive. If it is used
+with the *xleave* option, *chronyd* will send two extra packets instead of one.
*minstratum* _stratum_:::
When the synchronisation source is selected from available sources, sources
with lower stratum are normally slightly preferred. This option can be used to
@@ -187,9 +215,13 @@ avoid selecting that source. This is useful with low stratum sources that are
known to be unreliable or inaccurate and which should be used only when other
sources are unreachable.
*version* _version_:::
-This option sets the NTP version number used in packets sent to the server.
-This can be useful when the server runs an old NTP implementation that does not
-respond to newer versions. The default version number is 4.
+This option sets the NTP version of packets sent to the server. This can be
+useful when the server runs an old NTP implementation that does not respond to
+requests using a newer version. The default version depends on whether a key is
+specified by the *key* option and which authentication hash function the key
+is using. If the output size of the hash function is longer than 160 bits, the
+default version is 3 for compatibility with older *chronyd* servers. Otherwise,
+the default version is 4.
[[pool]]*pool* _name_ [_option_]...::
The syntax of this directive is similar to that for the <<server,*server*>>
@@ -218,26 +250,33 @@ pool pool.ntp.org iburst maxsources 3
[[peer]]*peer* _hostname_ [_option_]...::
The syntax of this directive is identical to that for the <<server,*server*>>
-directive, except that it is used to specify an NTP peer rather than an NTP
-server.
+directive, except that it specifies a symmetric association with an NTP peer
+instead of a client/server association with an NTP server. A single symmetric
+association allows the peers to be both servers and clients to each other. This
+is mainly useful when the NTP implementation of the peer (e.g. *ntpd*) supports
+ephemeral symmetric associations and does not need to be configured with an
+address of this host. *chronyd* does not support ephemeral associations.
+
When a key is specified by the *key* option to enable authentication, both
-peers must be configured to use the same key and the same key number.
-+
-Please note that NTP peers that are not configured with a key to enable
-authentication are vulnerable to a denial-of-service attack. An attacker
-knowing that NTP hosts A and B are peering with each other can send a packet
-with random timestamps to host A with source address of B which will set the
-NTP state variables on A to the values sent by the attacker. Host A will then
-send on its next poll to B a packet with an origin timestamp that does not match
-the transmit timestamp of B and the packet will be dropped. If the attacker
-does this periodically for both hosts, they will not be able to synchronise to
-each other.
-+
-This attack can be prevented by enabling authentication with the *key* option,
-or by using the <<server,*server*>> directive on both sides to specify the other
-host as a server instead of a peer. The disadvantage of the *server* directive
-is that it will double the network traffic between the two hosts.
+peers must use the same key and the same key number.
++
+Note that the symmetric mode is less secure than the client/server mode. A
+denial-of-service attack is possible on unauthenticated symmetric associations,
+i.e. when the peer was specified without the *key* option. An attacker who does
+not see network traffic between two hosts, but knows that they are peering with
+each other, can periodically send them unauthenticated packets with spoofed
+source addresses in order to disrupt their NTP state and prevent them from
+synchronising to each other. When the association is authenticated, an attacker
+who does see the network traffic, but cannot prevent the packets from reaching
+the other host, can still disrupt the state by replaying old packets. The
+attacker has effectively the same power as a man-in-the-middle attacker. A
+partial protection against this attack is implemented in *chronyd*, which can
+protect the peers if they are using the same polling interval and they never
+sent an authenticated packet with a timestamp from future, but it should not be
+relied on as it is difficult to ensure the conditions are met. If two hosts
+should be able to synchronise to each other in both directions, it is
+recommended to use two separate client/server associations (specified by the
+<<server,*server*>> directive on both hosts) instead.
[[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...::
In normal operation, *chronyd* slews the time when it needs to adjust the
@@ -395,6 +434,11 @@ This option sets the rate of the pulses in the PPS signal (in Hz). This option
controls how the pulses will be completed with real time. To actually receive
more than one pulse per second, a negative *dpoll* has to be specified (-3 for
a 5Hz signal). The default is 1.
+*maxlockage* _pulses_:::
+This option specifies in number of pulses how old can be samples from the
+refclock specified by the *lock* option to be paired with the pulses.
+Increasing this value is useful when the samples are produced at a lower rate
+than the pulses. The default is 2.
*offset* _offset_:::
This option can be used to compensate for a constant error. The specified
offset (in seconds) is applied to all samples produced by the reference clock.
@@ -506,12 +550,12 @@ saved.
An example of the directive is:
+
----
-dumpdir @CHRONYVARDIR@
+dumpdir @CHRONYRUNDIR@
----
+
-A source whose reference ID (the IP address for IPv4 sources) is _1.2.3.4_
-would have its measurement history saved in the file
-_/var/lib/chrony/1.2.3.4.dat_.
+A source whose IP address is _1.2.3.4_ would have its measurement history saved
+in the file _@CHRONYRUNDIR@/1.2.3.4.dat_. History of reference clocks is saved
+to files named by their reference ID in form of _refid:XXXXXXXX.dat_.
[[dumponexit]]*dumponexit*::
If this directive is present, it indicates that *chronyd* should save the
@@ -529,7 +573,7 @@ useful range is 4 to 64.
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 0. The useful range is 4 to 64.
+directives. The default value is 6. The useful range is 4 to 64.
=== Source selection
@@ -561,6 +605,13 @@ Setting *maxdistance* to a larger value can be useful to allow synchronisation
with a server that only has a very infrequent connection to its sources and can
accumulate a large dispersion between updates of its clock.
+[[maxjitter]]*maxjitter* _jitter_::
+The *maxjitter* directive sets the maximum allowed jitter of the sources to not
+be rejected by the source selection algorithm. This prevents synchronisation
+with sources that have a small root distance, but their time is too variable.
++
+By default, the maximum jitter is 1 second.
+
[[minsources]]*minsources* _sources_::
The *minsources* directive sets the minimum number of sources that need to be
considered as selectable in the source selection algorithm before the local
@@ -1008,25 +1059,20 @@ There is also a *deny all* directive with similar behaviour to the *allow all*
directive.
[[bindaddress]]*bindaddress* _address_::
-The *bindaddress* directive allows you to restrict the network interface to
-which *chronyd* will listen for NTP requests. This provides an additional level
-of access restriction above that available through the <<deny,*deny*>>
-mechanism.
+The *bindaddress* directive binds the socket on which *chronyd* listens for NTP
+requests to a local address of the computer. On systems other than Linux, the
+address of the computer needs to be already configured when *chronyd* is
+started.
+
-Suppose you have a local network with addresses in the _192.168.1.0_
-subnet together with an Internet connection. The network interface's IP
-address is _192.168.1.1_. Suppose you want to block all access through the
-Internet connection. You could add the line:
+An example of the use of the directive is:
+
----
bindaddress 192.168.1.1
----
+
-to the configuration file.
-+
-For each of the IPv4 and IPv6 protocols, only one *bindaddress* directive can be
-specified. Therefore, it is not useful on computers which should serve NTP on
-multiple network interfaces.
+Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
+directive can be specified. Therefore, it is not useful on computers which
+should serve NTP on multiple network interfaces.
[[broadcast]]*broadcast* _interval_ _address_ [_port_]::
The *broadcast* directive is used to declare a broadcast address to which
@@ -1065,8 +1111,10 @@ directive.
[[clientloglimit]]*clientloglimit* _limit_::
This directive specifies the maximum amount of memory that *chronyd* is allowed
-to allocate for logging of client accesses. The default limit is 524288 bytes,
-which allows monitoring of several thousands of addresses at the same time.
+to allocate for logging of client accesses and the state that *chronyd* as an
+NTP server needs to support the interleaved mode for its clients. The default
+limit is 524288 bytes, which is sufficient for monitoring about four thousand
+clients at the same time.
+
In older *chrony* versions if the limit was set to 0, the memory allocation was
unlimited.
@@ -1080,7 +1128,8 @@ clientloglimit 1048576
[[noclientlog]]*noclientlog*::
This directive, which takes no arguments, specifies that client accesses are
not to be logged. Normally they are logged, allowing statistics to be reported
-using the <<chronyc.adoc#clients,*clients*>> command in *chronyc*.
+using the <<chronyc.adoc#clients,*clients*>> command in *chronyc*. This option
+also effectively disables server support for the NTP interleaved mode.
[[local]]*local* [_option_]...::
The *local* directive enables a local reference mode, which allows *chronyd*
@@ -1142,6 +1191,23 @@ An example of the directive is:
local stratum 10 orphan
----
+[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
+This directive specifies the location of the Samba *ntp_signd* socket when it
+is running as a Domain Controller (DC). If *chronyd* is compiled with this
+feature, responses to MS-SNTP clients will be signed by the *smbd* daemon.
++
+Note that MS-SNTP requests are not authenticated and any client that is allowed
+to access the server by the <<allow,*allow*>> directive, or the
+<<chronyc.adoc#allow,*allow*>> command in *chronyc*, can get an MS-SNTP
+response signed with a trust account's password and try to crack the password
+in a brute-force attack. Access to the server should be carefully controlled.
++
+An example of the directive is:
++
+----
+ntpsigndsocket /var/lib/samba/ntp_signd
+----
+
[[port]]*port* _port_::
This option allows you to configure the port on which *chronyd* will listen for
NTP requests. The port will be open only when an address is allowed by the
@@ -1251,16 +1317,17 @@ smoothtime 50000 0.01
=== Command and monitoring access
[[bindcmdaddress]]*bindcmdaddress* _address_::
-The *bindcmdaddress* directive allows you to specify the network interface on
-which *chronyd* will listen for monitoring command packets (issued by
-*chronyc*). This provides an additional level of access restriction above that
-available through the <<cmddeny,*cmddeny*>> mechanism.
+The *bindcmdaddress* directive allows you to specify an IP address of an
+interface on which *chronyd* will listen for monitoring command packets (issued
+by *chronyc*). On systems other than Linux, the address of the interface needs
+to be already configured when *chronyd* is started.
+
This directive can also change the path of the Unix domain command socket,
which is used by *chronyc* to send configuration commands. The socket must be
in a directory that is accessible only by the root or _chrony_ user. The
directory will be created on start if it does not exist. The compiled-in default
-path of the socket is _@CHRONYSOCKDIR@/chronyd.sock_.
+path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be
+disabled by setting the path to _/_.
+
By default, *chronyd* binds to the loopback interface (with addresses
_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen
@@ -1273,8 +1340,8 @@ bindcmdaddress ::
+
to the configuration file.
+
-For each of the IPv4 and IPv6 protocols, only one *bindcmdaddress* directive can be
-specified.
+For each of the IPv4, IPv6, and Unix domain protocols, only one
+*bindcmdaddress* directive can be specified.
+
An example that sets the path of the Unix domain command socket is:
+
@@ -1368,7 +1435,7 @@ This would set the threshold error to 30 seconds.
[[rtcdevice]]*rtcdevice* _device_::
The *rtcdevice* directive sets the path to the device file for accessing the
-RTC. The default path is _/dev/rtc_.
+RTC. The default path is _@DEFAULT_RTC_DEVICE@_.
[[rtcfile]]*rtcfile* _file_::
The *rtcfile* directive defines the name of the file in which *chronyd* can
@@ -1421,7 +1488,7 @@ cannot be used with the <<rtcfile,*rtcfile*>> directive.
+
On Linux, the RTC copy is performed by the kernel every 11 minutes.
+
-On Mac OS X, <<chronyd,*chronyd*>> will perform the RTC copy every 60 minutes
+On macOS, <<chronyd,*chronyd*>> will perform the RTC copy every 60 minutes
when the system clock is in a synchronised state.
+
On other systems this directive does nothing.
@@ -1440,8 +1507,8 @@ called _measurements.log_. An example line (which actually appears as a single
line in the file) from the log file is shown below.
+
----
-2015-10-13 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
- -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03
+2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
+ -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 CB00717B 4B D K
----
+
The columns are as follows (the quantities in square brackets are the values
@@ -1471,6 +1538,13 @@ from the example line above):
. The peer dispersion (_epsilon_ in RFC 5905). [1.577e-05]
. The root delay (_DELTA_ in RFC 5905). [1.615e-01]
. The root dispersion (_EPSILON_ in RFC 5905). [7.446e-03]
+. Reference ID of the server's source as a hexadecimal number. [CB00717B]
+. NTP mode of the received packet (_1_=active peer, _2_=passive peer,
+ _3_=server, _B_=basic, _I_=interleaved). [4B]
+. Source of the local transmit timestamp
+ (_D_=daemon, _K_=kernel, _H_=hardware). [D]
+. Source of the local receive timestamp
+ (_D_=daemon, _K_=kernel, _H_=hardware). [K]
+
*statistics*:::
This option logs information about the regression processing to a file called
@@ -1478,8 +1552,8 @@ _statistics.log_. An example line (which actually appears as a single line in
the file) from the log file is shown below.
+
----
-2015-07-22 05:40:50 203.0.113.15 6.261e-03 -3.247e-03 \
- 2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8
+2016-08-10 05:40:50 203.0.113.15 6.261e-03 -3.247e-03 \
+ 2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8 0.00
----
+
The columns are as follows (the quantities in square brackets are the values
@@ -1514,6 +1588,11 @@ from the example line above):
to be discarded. The number of runs for the data that is being retained is
tabulated. Values of approximately half the number of samples are expected.
[8]
+. The estimated asymmetry of network jitter on the path to the source which was
+ used to correct the measured offsets. The asymmetry can be between -0.5 and
+ 0.5. A negative value means the delay of packets sent to the source is
+ more variable than the delay of packets sent from the source back. [0.00,
+ i.e. no correction for asymmetry]
+
*tracking*:::
This option logs changes to the estimate of the system's gain or loss rate, and
@@ -1693,6 +1772,39 @@ sendmail binary.
=== Miscellaneous
+[[hwtimestamp]]*hwtimestamp* _interface_::
+This directive enables hardware timestamping of NTP packets sent to and
+received from the specified network interface. The network interface controller
+(NIC) uses its own clock to accurately timestamp the actual transmissions and
+receptions, avoiding processing and queueing delays in the kernel, network
+driver, and hardware. This can significantly improve the accuracy of the
+timestamps and the measured offset, which is used for synchronisation of the
+system clock. In order to get best results, it is necessary to enable HW
+timestamping on both sides receiving and sending the packets (i.e. server and
+client, or both peers), and also enable the interleaved mode with the *xleave*
+option in the <<server,*server*>> or the <<peer,*peer*>> directive.
++
+This directive is supported on Linux 3.19 and newer. The NIC must support HW
+timestamping, which can be verified with the *ethtool -T* command. The list of
+capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
+_SOF_TIMESTAMPING_TX_HARDWARE_, _SOF_TIMESTAMPING_RX_HARDWARE_, and the filter
+modes should have _HWTSTAMP_FILTER_ALL_. When *chronyd* is running, no other
+process should be working with the clock on the NIC. If no *hwtimestamp*
+directive is specified, *chronyd* will try to use software (kernel)
+timestamping. With both hardware and software timestamping there are
+some limitations on which packets can be actually timestamped, e.g. transmit
+timestamping does not currently work with IPv6 packets using IP options and
+hardware receive timestamping does not work with packets from bridged
+interfaces. The timestamping used in measurements is indicated in the
+_measurements.log_ file if enabled by the <<log,*log measurements*>> directive,
+and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in *chronyc*.
++
+An example of the directive is:
++
+----
+hwtimestamp eth0
+----
+
[[include]]*include* _pattern_::
The *include* directive includes a configuration file or multiple configuration
files if a wildcard pattern is specified. This can be useful when maintaining
@@ -1755,8 +1867,8 @@ significant impact on performance as *chronyd's* memory usage is modest. The
[[pidfile]]*pidfile* _file_::
*chronyd* always writes its process ID (PID) to a file, and checks this file on
-startup to see if another *chronyd* may already be running on the system. By
-default, the file used is _/var/run/chronyd.pid_. The *pidfile* directive
+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.:
+
----
@@ -1765,8 +1877,8 @@ 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 Mac
-OS X, this option must have either a value of 0 (the default) to disable the
+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.
+
@@ -1781,7 +1893,7 @@ wait for the scheduler to get around to running it. You should not use this
unless you really need it. The *sched_setscheduler(2)* man page has more
details.
+
-On Mac OS X, this directive uses the *thread_policy_set()* kernel call to
+On macOS, this directive uses the *thread_policy_set()* kernel call to
specify real-time scheduling. As noted for Linux, you should not use this
directive unless you really need it.
@@ -1790,7 +1902,7 @@ The *user* directive sets the name of the system user to which *chronyd* will
switch after start in order to drop root privileges.
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
-On Mac OS X, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
+On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
+
@@ -2094,6 +2206,34 @@ For the system shutdown, *chronyd* should receive a SIGTERM several seconds
before the final SIGKILL; the SIGTERM causes the measurement histories and RTC
information to be saved.
+=== Public NTP server
+
+*chronyd* can be configured to operate as a public NTP server, e.g. to join the
+http://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration
+is similar to the NTP client with permanent connection, except it needs to
+allow client access from all addresses. It is recommended to handpick at least
+few good servers, and possibly combine them with a random selection of other
+servers in the pool. Rate limiting can be enabled to not waste too much
+bandwidth on misconfigured and broken NTP clients. The *-r* option with the
+*dumpdir* directive shortens the time for which *chronyd* will not serve time
+to its clients when it needs to be restarted for any reason.
+
+The configuration file might be:
+
+----
+server foo.example.net iburst
+server bar.example.net iburst
+server baz.example.net iburst
+pool pool.ntp.org iburst
+makestep 1.0 3
+rtcsync
+allow
+ratelimit interval 2 burst 10
+driftfile @CHRONYVARDIR@/drift
+dumpdir @CHRONYRUNDIR@
+dumponexit
+----
+
== SEE ALSO
<<chronyc.adoc#,*chronyc(1)*>>, <<chronyd.adoc#,*chronyd(8)*>>
diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in
index edc5847..b365003 100644
--- a/doc/chrony.conf.man.in
+++ b/doc/chrony.conf.man.in
@@ -2,12 +2,12 @@
.\" Title: chrony.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: Asciidoctor 1.5.4
-.\" Date: 2016-11-21
+.\" Date: 2016-12-09
.\" Manual: Configuration Files
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONY.CONF" "5" "2016-11-21" "chrony @CHRONY_VERSION@" "Configuration Files"
+.TH "CHRONY.CONF" "5" "2016-12-09" "chrony @CHRONY_VERSION@" "Configuration Files"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -132,6 +132,16 @@ the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0.
.RE
.sp
+\fBoffset\fP \fIoffset\fP
+.RS 4
+This option specifies a correction (in seconds) which will be applied to
+offsets measured with this source. It\(cqs particularly useful to compensate for a
+known asymmetry in network delay or timestamping errors. For example, if
+packets sent to the source were on average delayed by 100 microseconds more
+than packets sent from the source back, the correction would be \-0.00005 (\-50
+microseconds). The default is 0.0.
+.RE
+.sp
\fBminsamples\fP \fIsamples\fP
.RS 4
Set the minimum number of samples kept for this source. This overrides the
@@ -189,12 +199,33 @@ order to improve the accuracy of the clock. They can be selected and used for
synchronisation only if they agree with the trusted and required source.
.RE
.sp
+\fBxleave\fP
+.RS 4
+This option enables an interleaved mode which allows the server or the peer to
+send transmit timestamps captured after the actual transmission (e.g. when the
+server or the peer is running \fBchronyd\fP with HW timestamping enabled by the
+\fBhwtimestamp\fP directive). This can significantly improve the
+accuracy of the measurements.
+.sp
+The interleaved mode is compatible with servers that support only the basic
+mode, but peers must both support and have enabled the interleaved mode,
+otherwise the synchronisation will work only in one direction. Note that even
+servers that support the interleaved mode might respond in the basic mode as
+the interleaved mode requires the servers to keep some state for each client
+and the state might be dropped when there are too many clients (e.g.
+\fBclientloglimit\fP is too small), or it might be overwritten
+by other clients that have the same IP address (e.g. computers behind NAT or
+someone sending requests with a spoofed source address). The \fBpresend\fP option
+can be used to shorten the interval in which the server has to keep the state
+for this computer and be able to respond in the interleaved mode.
+.RE
+.sp
\fBpolltarget\fP \fItarget\fP
.RS 4
Target number of measurements to use for the regression algorithm which
\fBchronyd\fP will try to maintain by adjusting the polling interval between
\fBminpoll\fP and \fBmaxpoll\fP. A higher target makes \fBchronyd\fP prefer shorter polling
-intervals. The default is 6 and a useful range is from 6 to 60.
+intervals. The default is 8 and a useful range is from 6 to 60.
.RE
.sp
\fBport\fP \fIport\fP
@@ -230,8 +261,11 @@ presend 9
.\}
.sp
when the polling interval is 512 seconds or more, an extra NTP client packet
-will be sent to the server a short time (4 seconds) before making the actual
+will be sent to the server a short time (2 seconds) before making the actual
measurement.
+.sp
+The \fBpresend\fP option cannot be used in the \fBpeer\fP directive. If it is used
+with the \fBxleave\fP option, \fBchronyd\fP will send two extra packets instead of one.
.RE
.sp
\fBminstratum\fP \fIstratum\fP
@@ -246,9 +280,13 @@ sources are unreachable.
.sp
\fBversion\fP \fIversion\fP
.RS 4
-This option sets the NTP version number used in packets sent to the server.
-This can be useful when the server runs an old NTP implementation that does not
-respond to newer versions. The default version number is 4.
+This option sets the NTP version of packets sent to the server. This can be
+useful when the server runs an old NTP implementation that does not respond to
+requests using a newer version. The default version depends on whether a key is
+specified by the \fBkey\fP option and which authentication hash function the key
+is using. If the output size of the hash function is longer than 160 bits, the
+default version is 3 for compatibility with older \fBchronyd\fP servers. Otherwise,
+the default version is 4.
.RE
.RE
.sp
@@ -288,26 +326,33 @@ pool pool.ntp.org iburst maxsources 3
\fBpeer\fP \fIhostname\fP [\fIoption\fP]...
.RS 4
The syntax of this directive is identical to that for the \fBserver\fP
-directive, except that it is used to specify an NTP peer rather than an NTP
-server.
+directive, except that it specifies a symmetric association with an NTP peer
+instead of a client/server association with an NTP server. A single symmetric
+association allows the peers to be both servers and clients to each other. This
+is mainly useful when the NTP implementation of the peer (e.g. \fBntpd\fP) supports
+ephemeral symmetric associations and does not need to be configured with an
+address of this host. \fBchronyd\fP does not support ephemeral associations.
.sp
When a key is specified by the \fBkey\fP option to enable authentication, both
-peers must be configured to use the same key and the same key number.
-.sp
-Please note that NTP peers that are not configured with a key to enable
-authentication are vulnerable to a denial\-of\-service attack. An attacker
-knowing that NTP hosts A and B are peering with each other can send a packet
-with random timestamps to host A with source address of B which will set the
-NTP state variables on A to the values sent by the attacker. Host A will then
-send on its next poll to B a packet with an origin timestamp that does not match
-the transmit timestamp of B and the packet will be dropped. If the attacker
-does this periodically for both hosts, they will not be able to synchronise to
-each other.
-.sp
-This attack can be prevented by enabling authentication with the \fBkey\fP option,
-or by using the \fBserver\fP directive on both sides to specify the other
-host as a server instead of a peer. The disadvantage of the \fBserver\fP directive
-is that it will double the network traffic between the two hosts.
+peers must use the same key and the same key number.
+.sp
+Note that the symmetric mode is less secure than the client/server mode. A
+denial\-of\-service attack is possible on unauthenticated symmetric associations,
+i.e. when the peer was specified without the \fBkey\fP option. An attacker who does
+not see network traffic between two hosts, but knows that they are peering with
+each other, can periodically send them unauthenticated packets with spoofed
+source addresses in order to disrupt their NTP state and prevent them from
+synchronising to each other. When the association is authenticated, an attacker
+who does see the network traffic, but cannot prevent the packets from reaching
+the other host, can still disrupt the state by replaying old packets. The
+attacker has effectively the same power as a man\-in\-the\-middle attacker. A
+partial protection against this attack is implemented in \fBchronyd\fP, which can
+protect the peers if they are using the same polling interval and they never
+sent an authenticated packet with a timestamp from future, but it should not be
+relied on as it is difficult to ensure the conditions are met. If two hosts
+should be able to synchronise to each other in both directions, it is
+recommended to use two separate client/server associations (specified by the
+\fBserver\fP directive on both hosts) instead.
.RE
.sp
\fBinitstepslew\fP \fIstep\-threshold\fP [\fIhostname\fP]...
@@ -526,6 +571,14 @@ more than one pulse per second, a negative \fBdpoll\fP has to be specified (\-3
a 5Hz signal). The default is 1.
.RE
.sp
+\fBmaxlockage\fP \fIpulses\fP
+.RS 4
+This option specifies in number of pulses how old can be samples from the
+refclock specified by the \fBlock\fP option to be paired with the pulses.
+Increasing this value is useful when the samples are produced at a lower rate
+than the pulses. The default is 2.
+.RE
+.sp
\fBoffset\fP \fIoffset\fP
.RS 4
This option can be used to compensate for a constant error. The specified
@@ -686,15 +739,15 @@ An example of the directive is:
.RS 4
.\}
.nf
-dumpdir @CHRONYVARDIR@
+dumpdir @CHRONYRUNDIR@
.fi
.if n \{\
.RE
.\}
.sp
-A source whose reference ID (the IP address for IPv4 sources) is \fI1.2.3.4\fP
-would have its measurement history saved in the file
-\fI/var/lib/chrony/1.2.3.4.dat\fP.
+A source whose IP address is \fI1.2.3.4\fP would have its measurement history saved
+in the file \fI@CHRONYRUNDIR@/1.2.3.4.dat\fP. History of reference clocks is saved
+to files named by their reference ID in form of \fIrefid:XXXXXXXX.dat\fP.
.RE
.sp
\fBdumponexit\fP
@@ -718,7 +771,7 @@ useful range is 4 to 64.
The \fBminsamples\fP directive sets the default minimum number of samples that
\fBchronyd\fP should keep for each source. This setting can be overridden for
individual sources in the \fBserver\fP and \fBrefclock\fP
-directives. The default value is 0. The useful range is 4 to 64.
+directives. The default value is 6. The useful range is 4 to 64.
.RE
.SS "Source selection"
.sp
@@ -754,6 +807,15 @@ with a server that only has a very infrequent connection to its sources and can
accumulate a large dispersion between updates of its clock.
.RE
.sp
+\fBmaxjitter\fP \fIjitter\fP
+.RS 4
+The \fBmaxjitter\fP directive sets the maximum allowed jitter of the sources to not
+be rejected by the source selection algorithm. This prevents synchronisation
+with sources that have a small root distance, but their time is too variable.
+.sp
+By default, the maximum jitter is 1 second.
+.RE
+.sp
\fBminsources\fP \fIsources\fP
.RS 4
The \fBminsources\fP directive sets the minimum number of sources that need to be
@@ -1330,15 +1392,12 @@ directive.
.sp
\fBbindaddress\fP \fIaddress\fP
.RS 4
-The \fBbindaddress\fP directive allows you to restrict the network interface to
-which \fBchronyd\fP will listen for NTP requests. This provides an additional level
-of access restriction above that available through the \fBdeny\fP
-mechanism.
+The \fBbindaddress\fP directive binds the socket on which \fBchronyd\fP listens for NTP
+requests to a local address of the computer. On systems other than Linux, the
+address of the computer needs to be already configured when \fBchronyd\fP is
+started.
.sp
-Suppose you have a local network with addresses in the \fI192.168.1.0\fP
-subnet together with an Internet connection. The network interface\(cqs IP
-address is \fI192.168.1.1\fP. Suppose you want to block all access through the
-Internet connection. You could add the line:
+An example of the use of the directive is:
.sp
.if n \{\
.RS 4
@@ -1350,11 +1409,9 @@ bindaddress 192.168.1.1
.RE
.\}
.sp
-to the configuration file.
-.sp
-For each of the IPv4 and IPv6 protocols, only one \fBbindaddress\fP directive can be
-specified. Therefore, it is not useful on computers which should serve NTP on
-multiple network interfaces.
+Currently, for each of the IPv4 and IPv6 protocols, only one \fBbindaddress\fP
+directive can be specified. Therefore, it is not useful on computers which
+should serve NTP on multiple network interfaces.
.RE
.sp
\fBbroadcast\fP \fIinterval\fP \fIaddress\fP [\fIport\fP]
@@ -1403,8 +1460,10 @@ directive.
\fBclientloglimit\fP \fIlimit\fP
.RS 4
This directive specifies the maximum amount of memory that \fBchronyd\fP is allowed
-to allocate for logging of client accesses. The default limit is 524288 bytes,
-which allows monitoring of several thousands of addresses at the same time.
+to allocate for logging of client accesses and the state that \fBchronyd\fP as an
+NTP server needs to support the interleaved mode for its clients. The default
+limit is 524288 bytes, which is sufficient for monitoring about four thousand
+clients at the same time.
.sp
In older \fBchrony\fP versions if the limit was set to 0, the memory allocation was
unlimited.
@@ -1426,7 +1485,8 @@ clientloglimit 1048576
.RS 4
This directive, which takes no arguments, specifies that client accesses are
not to be logged. Normally they are logged, allowing statistics to be reported
-using the \fBclients\fP command in \fBchronyc\fP.
+using the \fBclients\fP command in \fBchronyc\fP. This option
+also effectively disables server support for the NTP interleaved mode.
.RE
.sp
\fBlocal\fP [\fIoption\fP]...
@@ -1514,6 +1574,31 @@ local stratum 10 orphan
.\}
.RE
.sp
+\fBntpsigndsocket\fP \fIdirectory\fP
+.RS 4
+This directive specifies the location of the Samba \fBntp_signd\fP socket when it
+is running as a Domain Controller (DC). If \fBchronyd\fP is compiled with this
+feature, responses to MS\-SNTP clients will be signed by the \fBsmbd\fP daemon.
+.sp
+Note that MS\-SNTP requests are not authenticated and any client that is allowed
+to access the server by the \fBallow\fP directive, or the
+\fBallow\fP command in \fBchronyc\fP, can get an MS\-SNTP
+response signed with a trust account\(cqs password and try to crack the password
+in a brute\-force attack. Access to the server should be carefully controlled.
+.sp
+An example of the directive is:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+ntpsigndsocket /var/lib/samba/ntp_signd
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.sp
\fBport\fP \fIport\fP
.RS 4
This option allows you to configure the port on which \fBchronyd\fP will listen for
@@ -1658,16 +1743,17 @@ smoothtime 50000 0.01
.sp
\fBbindcmdaddress\fP \fIaddress\fP
.RS 4
-The \fBbindcmdaddress\fP directive allows you to specify the network interface on
-which \fBchronyd\fP will listen for monitoring command packets (issued by
-\fBchronyc\fP). This provides an additional level of access restriction above that
-available through the \fBcmddeny\fP mechanism.
+The \fBbindcmdaddress\fP directive allows you to specify an IP address of an
+interface on which \fBchronyd\fP will listen for monitoring command packets (issued
+by \fBchronyc\fP). On systems other than Linux, the address of the interface needs
+to be already configured when \fBchronyd\fP is started.
.sp
This directive can also change the path of the Unix domain command socket,
which is used by \fBchronyc\fP to send configuration commands. The socket must be
in a directory that is accessible only by the root or \fIchrony\fP user. The
directory will be created on start if it does not exist. The compiled\-in default
-path of the socket is \fI@CHRONYSOCKDIR@/chronyd.sock\fP.
+path of the socket is \fI@CHRONYRUNDIR@/chronyd.sock\fP. The socket can be
+disabled by setting the path to \fI/\fP.
.sp
By default, \fBchronyd\fP binds to the loopback interface (with addresses
\fI127.0.0.1\fP and \fI::1\fP). This blocks all access except from localhost. To listen
@@ -1686,8 +1772,8 @@ bindcmdaddress ::
.sp
to the configuration file.
.sp
-For each of the IPv4 and IPv6 protocols, only one \fBbindcmdaddress\fP directive can be
-specified.
+For each of the IPv4, IPv6, and Unix domain protocols, only one
+\fBbindcmdaddress\fP directive can be specified.
.sp
An example that sets the path of the Unix domain command socket is:
.sp
@@ -1824,7 +1910,7 @@ This would set the threshold error to 30 seconds.
\fBrtcdevice\fP \fIdevice\fP
.RS 4
The \fBrtcdevice\fP directive sets the path to the device file for accessing the
-RTC. The default path is \fI/dev/rtc\fP.
+RTC. The default path is \fI@DEFAULT_RTC_DEVICE@\fP.
.RE
.sp
\fBrtcfile\fP \fIfile\fP
@@ -1918,7 +2004,7 @@ cannot be used with the \fBrtcfile\fP directive.
.sp
On Linux, the RTC copy is performed by the kernel every 11 minutes.
.sp
-On Mac OS X, \fBchronyd\fP will perform the RTC copy every 60 minutes
+On macOS, \fBchronyd\fP will perform the RTC copy every 60 minutes
when the system clock is in a synchronised state.
.sp
On other systems this directive does nothing.
@@ -1942,8 +2028,8 @@ line in the file) from the log file is shown below.
.RS 4
.\}
.nf
-2015\-10\-13 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \(rs
- \-4.966e\-03 2.296e\-01 1.577e\-05 1.615e\-01 7.446e\-03
+2016\-11\-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \(rs
+ \-4.966e\-03 2.296e\-01 1.577e\-05 1.615e\-01 7.446e\-03 CB00717B 4B D K
.fi
.if n \{\
.RE
@@ -2135,6 +2221,53 @@ The root delay (\fIDELTA\fP in RFC 5905). [1.615e\-01]
.\}
The root dispersion (\fIEPSILON\fP in RFC 5905). [7.446e\-03]
.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 17.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP " 17." 4.2
+.\}
+Reference ID of the server\(cqs source as a hexadecimal number. [CB00717B]
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 18.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP " 18." 4.2
+.\}
+NTP mode of the received packet (\fI1\fP=active peer, \fI2\fP=passive peer,
+\fI3\fP=server, \fIB\fP=basic, \fII\fP=interleaved). [4B]
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 19.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP " 19." 4.2
+.\}
+Source of the local transmit timestamp
+(\fID\fP=daemon, \fIK\fP=kernel, \fIH\fP=hardware). [D]
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 20.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP " 20." 4.2
+.\}
+Source of the local receive timestamp
+(\fID\fP=daemon, \fIK\fP=kernel, \fIH\fP=hardware). [K]
+.RE
.RE
.sp
\fBstatistics\fP
@@ -2147,8 +2280,8 @@ the file) from the log file is shown below.
.RS 4
.\}
.nf
-2015\-07\-22 05:40:50 203.0.113.15 6.261e\-03 \-3.247e\-03 \(rs
- 2.220e\-03 1.874e\-06 1.080e\-06 7.8e\-02 16 0 8
+2016\-08\-10 05:40:50 203.0.113.15 6.261e\-03 \-3.247e\-03 \(rs
+ 2.220e\-03 1.874e\-06 1.080e\-06 7.8e\-02 16 0 8 0.00
.fi
.if n \{\
.RE
@@ -2305,6 +2438,21 @@ to be discarded. The number of runs for the data that is being retained is
tabulated. Values of approximately half the number of samples are expected.
[8]
.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 13.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP " 13." 4.2
+.\}
+The estimated asymmetry of network jitter on the path to the source which was
+used to correct the measured offsets. The asymmetry can be between \-0.5 and
+0.5. A negative value means the delay of packets sent to the source is
+more variable than the delay of packets sent from the source back. [0.00,
+i.e. no correction for asymmetry]
+.RE
.RE
.sp
\fBtracking\fP
@@ -2877,6 +3025,47 @@ sendmail binary.
.RE
.SS "Miscellaneous"
.sp
+\fBhwtimestamp\fP \fIinterface\fP
+.RS 4
+This directive enables hardware timestamping of NTP packets sent to and
+received from the specified network interface. The network interface controller
+(NIC) uses its own clock to accurately timestamp the actual transmissions and
+receptions, avoiding processing and queueing delays in the kernel, network
+driver, and hardware. This can significantly improve the accuracy of the
+timestamps and the measured offset, which is used for synchronisation of the
+system clock. In order to get best results, it is necessary to enable HW
+timestamping on both sides receiving and sending the packets (i.e. server and
+client, or both peers), and also enable the interleaved mode with the \fBxleave\fP
+option in the \fBserver\fP or the \fBpeer\fP directive.
+.sp
+This directive is supported on Linux 3.19 and newer. The NIC must support HW
+timestamping, which can be verified with the \fBethtool \-T\fP command. The list of
+capabilities should include \fISOF_TIMESTAMPING_RAW_HARDWARE\fP,
+\fISOF_TIMESTAMPING_TX_HARDWARE\fP, \fISOF_TIMESTAMPING_RX_HARDWARE\fP, and the filter
+modes should have \fIHWTSTAMP_FILTER_ALL\fP. When \fBchronyd\fP is running, no other
+process should be working with the clock on the NIC. If no \fBhwtimestamp\fP
+directive is specified, \fBchronyd\fP will try to use software (kernel)
+timestamping. With both hardware and software timestamping there are
+some limitations on which packets can be actually timestamped, e.g. transmit
+timestamping does not currently work with IPv6 packets using IP options and
+hardware receive timestamping does not work with packets from bridged
+interfaces. The timestamping used in measurements is indicated in the
+\fImeasurements.log\fP file if enabled by the \fBlog measurements\fP directive,
+and the \fBntpdata\fP report in \fBchronyc\fP.
+.sp
+An example of the directive is:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+hwtimestamp eth0
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.sp
\fBinclude\fP \fIpattern\fP
.RS 4
The \fBinclude\fP directive includes a configuration file or multiple configuration
@@ -2964,8 +3153,8 @@ significant impact on performance as \fBchronyd\(cqs\fP memory usage is modest.
\fBpidfile\fP \fIfile\fP
.RS 4
\fBchronyd\fP always writes its process ID (PID) to a file, and checks this file on
-startup to see if another \fBchronyd\fP may already be running on the system. By
-default, the file used is \fI/var/run/chronyd.pid\fP. The \fBpidfile\fP directive
+startup to see if another \fBchronyd\fP might already be running on the system. By
+default, the file used is \fI@DEFAULT_PID_FILE@\fP. The \fBpidfile\fP directive
allows the name to be changed, e.g.:
.sp
.if n \{\
@@ -2982,8 +3171,8 @@ pidfile /run/chronyd.pid
\fBsched_priority\fP \fIpriority\fP
.RS 4
On Linux, the \fBsched_priority\fP directive will select the SCHED_FIFO real\-time
-scheduler at the specified priority (which must be between 0 and 100). On Mac
-OS X, this option must have either a value of 0 (the default) to disable the
+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.
.sp
@@ -2998,7 +3187,7 @@ wait for the scheduler to get around to running it. You should not use this
unless you really need it. The \fBsched_setscheduler(2)\fP man page has more
details.
.sp
-On Mac OS X, this directive uses the \fBthread_policy_set()\fP kernel call to
+On macOS, this directive uses the \fBthread_policy_set()\fP kernel call to
specify real\-time scheduling. As noted for Linux, you should not use this
directive unless you really need it.
.RE
@@ -3009,7 +3198,7 @@ The \fBuser\fP directive sets the name of the system user to which \fBchronyd\fP
switch after start in order to drop root privileges.
.sp
On Linux, \fBchronyd\fP needs to be compiled with support for the \fBlibcap\fP library.
-On Mac OS X, FreeBSD, NetBSD and Solaris \fBchronyd\fP forks into two processes.
+On macOS, FreeBSD, NetBSD and Solaris \fBchronyd\fP forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
.sp
@@ -3420,6 +3609,40 @@ configuration file.
For the system shutdown, \fBchronyd\fP should receive a SIGTERM several seconds
before the final SIGKILL; the SIGTERM causes the measurement histories and RTC
information to be saved.
+.SS "Public NTP server"
+.sp
+\fBchronyd\fP can be configured to operate as a public NTP server, e.g. to join the
+.URL "http://www.pool.ntp.org/en/join.html" "pool.ntp.org" " "
+project. The configuration
+is similar to the NTP client with permanent connection, except it needs to
+allow client access from all addresses. It is recommended to handpick at least
+few good servers, and possibly combine them with a random selection of other
+servers in the pool. Rate limiting can be enabled to not waste too much
+bandwidth on misconfigured and broken NTP clients. The \fB\-r\fP option with the
+\fBdumpdir\fP directive shortens the time for which \fBchronyd\fP will not serve time
+to its clients when it needs to be restarted for any reason.
+.sp
+The configuration file might be:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+server foo.example.net iburst
+server bar.example.net iburst
+server baz.example.net iburst
+pool pool.ntp.org iburst
+makestep 1.0 3
+rtcsync
+allow
+ratelimit interval 2 burst 10
+driftfile @CHRONYVARDIR@/drift
+dumpdir @CHRONYRUNDIR@
+dumponexit
+.fi
+.if n \{\
+.RE
+.\}
.SH "SEE ALSO"
.sp
\fBchronyc(1)\fP, \fBchronyd(8)\fP
diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc
index 27574ed..eee086b 100644
--- a/doc/chronyc.adoc
+++ b/doc/chronyc.adoc
@@ -44,7 +44,7 @@ There are two ways *chronyc* can access *chronyd*. One is the Internet
Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
accessible locally by the root or _chrony_ user. By default, *chronyc* first
tries to connect to the Unix domain socket. The compiled-in default path is
-_@CHRONYSOCKDIR@/chronyd.sock_. If that fails (e.g. because *chronyc* is
+_@CHRONYRUNDIR@/chronyd.sock_. If that fails (e.g. because *chronyc* is
running under a non-root user), it will try to connect to 127.0.0.1 and then
::1.
@@ -74,8 +74,8 @@ With this option hostnames will be resolved only to IPv4 addresses.
With this option hostnames will be resolved only to IPv6 addresses.
*-n*::
-This option disables resolving of IP addresses to hostnames (e.g. to avoid slow
-DNS lookups).
+This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
+DNS lookups. Long addresses will not be truncated to fit into the column.
*-c*::
This option enables printing of reports in a comma-separated values (CSV)
@@ -127,7 +127,7 @@ The *tracking* command displays parameters about the system's clock
performance. An example of the output is shown below.
+
----
-Reference ID : 203.0.113.15 (foo.example.net)
+Reference ID : CB00710F (foo.example.net)
Stratum : 3
Ref time (UTC) : Fri Feb 3 15:00:29 2012
System time : 0.000001501 seconds slow of NTP time
@@ -150,10 +150,14 @@ computer is currently synchronised. For IPv4 addresses, the reference ID is
equal to the address and for IPv6 addresses it is the first 32 bits of the MD5
sum of the address.
+
-If it is _127.127.1.1_ it means the computer is not synchronised to any
-external source and that you have the _local_ mode operating (via the
-<<local,*local*>> command in *chronyc*, or the
+If the reference ID is _7F7F0101_ and there is no name or IP address, it means
+the computer is not synchronised to any external source and that you have the
+_local_ mode operating (via the <<local,*local*>> command in *chronyc*, or the
<<chrony.conf.adoc#local,*local*>> directive in the configuration file).
++
+The reference ID is printed as a hexadecimal number. Note that in older
+versions it used to be printed in quad-dotted notation and could be confused
+with an IPv4 address.
*Stratum*:::
The stratum indicates how many hops away from a computer with an attached
reference clock we are. Such a computer is a stratum-1 computer, so the
@@ -437,6 +441,92 @@ the offline state.
the name of the server or peer was not resolved to an address yet; this source is
not visible in the *sources* and *sourcestats* reports.
+[[ntpdata]]*ntpdata* _address_::
+The *ntpdata* command displays the last valid measurement and other
+NTP-specific information about the NTP source. An example of the output is
+shown below.
++
+----
+Remote address : 203.0.113.15 (CB00710F)
+Remote port : 123
+Local address : 203.0.113.74 (CB00714A)
+Leap status : Normal
+Version : 4
+Mode : Server
+Stratum : 1
+Poll : 10
+Precision : 0.000000060 seconds
+Root delay : 0.000015 seconds
+Root dispersion : 0.000015 seconds
+Reference ID : 50505331
+Reference time : Fri Nov 25 15:22:12 2016
+Offset : -0.000060878 seconds
+Peer delay : 0.000175634 seconds
+Peer dispersion : 0.000000681 seconds
+Response time : 0.000053050 seconds
+Jitter asymmetry: +0.00
+NTP tests : 111 111 1111
+Interleaved : No
+Authenticated : No
+TX timestamping : Kernel
+RX timestamping : Kernel
+Total TX : 24
+Total RX : 24
+Total valid RX : 24
+----
++
+The fields are explained as follows:
++
+*Remote address*:::
+The IP address of the NTP server or peer, and the corresponding reference ID.
+*Remote port*:::
+The UDP port number to which the request was sent. The standard NTP port is
+123.
+*Local address*:::
+The local IP address which received the response, and the corresponding
+reference ID.
+*Leap status*:::
+*Version*:::
+*Mode*:::
+*Stratum*:::
+*Poll*:::
+*Precision*:::
+*Root delay*:::
+*Root dispersion*:::
+*Reference ID*:::
+*Reference time*:::
+The NTP values from the last valid response.
+*Offset*:::
+*Peer delay*:::
+*Peer dispersion*:::
+The measured values.
+*Response time*:::
+The time the server or peer spent in processing of the request and waiting
+before sending the response.
+*Jitter asymmetry*:::
+The estimated asymmetry of network jitter on the path to the source. The
+asymmetry can be between -0.5 and 0.5. A negative value means the delay of
+packets sent to the source is more variable than the delay of packets sent
+from the source back.
+*NTP tests*:::
+Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
+delay, delay ratio, delay dev ratio, and synchronisation loop.
+*Interleaved*:::
+This shows if the response was in the interleaved mode.
+*Authenticated*:::
+This shows if the response was authenticated.
+*TX timestamping*:::
+The source of the local transmit timestamp. Valid values are _Daemon_,
+_Kernel_, and _Hardware_.
+*RX timestamping*:::
+The source of the local receive timestamp.
+*Total TX*:::
+The number of packets sent to the source.
+*Total RX*:::
+The number of all packets received from the source.
+*Total valid RX*:::
+The number of valid packets received from the source.
+
[[add_peer]]*add peer* _address_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
*chronyd* is running.
diff --git a/doc/chronyc.man.in b/doc/chronyc.man.in
index d9fed87..a3f870c 100644
--- a/doc/chronyc.man.in
+++ b/doc/chronyc.man.in
@@ -2,12 +2,12 @@
.\" Title: chronyc
.\" Author: [see the "AUTHORS" section]
.\" Generator: Asciidoctor 1.5.4
-.\" Date: 2016-11-21
+.\" Date: 2016-12-09
.\" Manual: User manual
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYC" "1" "2016-11-21" "chrony @CHRONY_VERSION@" "User manual"
+.TH "CHRONYC" "1" "2016-12-09" "chrony @CHRONY_VERSION@" "User manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -38,7 +38,7 @@ There are two ways \fBchronyc\fP can access \fBchronyd\fP. One is the Internet
Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
accessible locally by the root or \fIchrony\fP user. By default, \fBchronyc\fP first
tries to connect to the Unix domain socket. The compiled\-in default path is
-\fI@CHRONYSOCKDIR@/chronyd.sock\fP. If that fails (e.g. because \fBchronyc\fP is
+\fI@CHRONYRUNDIR@/chronyd.sock\fP. If that fails (e.g. because \fBchronyc\fP is
running under a non\-root user), it will try to connect to 127.0.0.1 and then
::1.
.sp
@@ -72,8 +72,8 @@ With this option hostnames will be resolved only to IPv6 addresses.
.sp
\fB\-n\fP
.RS 4
-This option disables resolving of IP addresses to hostnames (e.g. to avoid slow
-DNS lookups).
+This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
+DNS lookups. Long addresses will not be truncated to fit into the column.
.RE
.sp
\fB\-c\fP
@@ -144,7 +144,7 @@ performance. An example of the output is shown below.
.RS 4
.\}
.nf
-Reference ID : 203.0.113.15 (foo.example.net)
+Reference ID : CB00710F (foo.example.net)
Stratum : 3
Ref time (UTC) : Fri Feb 3 15:00:29 2012
System time : 0.000001501 seconds slow of NTP time
@@ -171,10 +171,14 @@ computer is currently synchronised. For IPv4 addresses, the reference ID is
equal to the address and for IPv6 addresses it is the first 32 bits of the MD5
sum of the address.
.sp
-If it is \fI127.127.1.1\fP it means the computer is not synchronised to any
-external source and that you have the \fIlocal\fP mode operating (via the
-\fBlocal\fP command in \fBchronyc\fP, or the
+If the reference ID is \fI7F7F0101\fP and there is no name or IP address, it means
+the computer is not synchronised to any external source and that you have the
+\fIlocal\fP mode operating (via the \fBlocal\fP command in \fBchronyc\fP, or the
\fBlocal\fP directive in the configuration file).
+.sp
+The reference ID is printed as a hexadecimal number. Note that in older
+versions it used to be printed in quad\-dotted notation and could be confused
+with an IPv4 address.
.RE
.sp
\fBStratum\fP
@@ -659,6 +663,133 @@ not visible in the \fBsources\fP and \fBsourcestats\fP reports.
.RE
.RE
.sp
+\fBntpdata\fP \fIaddress\fP
+.RS 4
+The \fBntpdata\fP command displays the last valid measurement and other
+NTP\-specific information about the NTP source. An example of the output is
+shown below.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+Remote address : 203.0.113.15 (CB00710F)
+Remote port : 123
+Local address : 203.0.113.74 (CB00714A)
+Leap status : Normal
+Version : 4
+Mode : Server
+Stratum : 1
+Poll : 10
+Precision : 0.000000060 seconds
+Root delay : 0.000015 seconds
+Root dispersion : 0.000015 seconds
+Reference ID : 50505331
+Reference time : Fri Nov 25 15:22:12 2016
+Offset : \-0.000060878 seconds
+Peer delay : 0.000175634 seconds
+Peer dispersion : 0.000000681 seconds
+Response time : 0.000053050 seconds
+Jitter asymmetry: +0.00
+NTP tests : 111 111 1111
+Interleaved : No
+Authenticated : No
+TX timestamping : Kernel
+RX timestamping : Kernel
+Total TX : 24
+Total RX : 24
+Total valid RX : 24
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The fields are explained as follows:
+.sp
+\fBRemote address\fP
+.RS 4
+The IP address of the NTP server or peer, and the corresponding reference ID.
+.RE
+.sp
+\fBRemote port\fP
+.RS 4
+The UDP port number to which the request was sent. The standard NTP port is
+123.
+.RE
+.sp
+\fBLocal address\fP
+.RS 4
+The local IP address which received the response, and the corresponding
+reference ID.
+.RE
+.sp
+\fBLeap status\fP, \fBVersion\fP, \fBMode\fP, \fBStratum\fP, \fBPoll\fP, \fBPrecision\fP, \fBRoot delay\fP, \fBRoot dispersion\fP, \fBReference ID\fP, \fBReference time\fP
+.RS 4
+The NTP values from the last valid response.
+.RE
+.sp
+\fBOffset\fP, \fBPeer delay\fP, \fBPeer dispersion\fP
+.RS 4
+The measured values.
+.RE
+.sp
+\fBResponse time\fP
+.RS 4
+The time the server or peer spent in processing of the request and waiting
+before sending the response.
+.RE
+.sp
+\fBJitter asymmetry\fP
+.RS 4
+The estimated asymmetry of network jitter on the path to the source. The
+asymmetry can be between \-0.5 and 0.5. A negative value means the delay of
+packets sent to the source is more variable than the delay of packets sent
+from the source back.
+.RE
+.sp
+\fBNTP tests\fP
+.RS 4
+Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
+delay, delay ratio, delay dev ratio, and synchronisation loop.
+.RE
+.sp
+\fBInterleaved\fP
+.RS 4
+This shows if the response was in the interleaved mode.
+.RE
+.sp
+\fBAuthenticated\fP
+.RS 4
+This shows if the response was authenticated.
+.RE
+.sp
+\fBTX timestamping\fP
+.RS 4
+The source of the local transmit timestamp. Valid values are \fIDaemon\fP,
+\fIKernel\fP, and \fIHardware\fP.
+.RE
+.sp
+\fBRX timestamping\fP
+.RS 4
+The source of the local receive timestamp.
+.RE
+.sp
+\fBTotal TX\fP
+.RS 4
+The number of packets sent to the source.
+.RE
+.sp
+\fBTotal RX\fP
+.RS 4
+The number of all packets received from the source.
+.RE
+.sp
+\fBTotal valid RX\fP
+.RS 4
+The number of valid packets received from the source.
+.RE
+.RE
+.sp
\fBadd peer\fP \fIaddress\fP [\fIoption\fP]...
.RS 4
The \fBadd peer\fP command allows a new NTP peer to be added whilst
diff --git a/doc/chronyd.adoc b/doc/chronyd.adoc
index d54d585..9cb8eff 100644
--- a/doc/chronyd.adoc
+++ b/doc/chronyd.adoc
@@ -75,14 +75,15 @@ This option is similar to *-q*, but it will only print the offset without any
corrections of the clock.
*-r*::
-This option will reload sample histories for each of the servers and refclocks
-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*>> directive in the configuration
-file. This option is useful if you want to stop and restart *chronyd* briefly
-for any reason, e.g. to install a new version. However, it should be used only
-on systems where the kernel can maintain clock compensation whilst not under
-*chronyd*'s control (i.e. Linux, FreeBSD, NetBSD and Solaris).
+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*>>
+directive in the configuration file. This option is useful if you want to stop
+and restart *chronyd* briefly for any reason, e.g. to install a new version.
+However, it should be used only on systems where the kernel can maintain clock
+compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD
+and Solaris).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -109,13 +110,20 @@ time and the RTC time, the system time will be set to it to restore the time
when *chronyd* was previously stopped. This is useful on computers that have no
RTC or the RTC is broken (e.g. it has no battery).
+*-t* _timeout_::
+This option sets a timeout (in seconds) after which *chronyd* will exit. If the
+clock is not synchronised, it will exit with a non-zero status. This is useful
+with the *-q* or *-Q* option to shorten the maximum time waiting for
+measurements, or with the *-r* option to limit the time when *chronyd* is
+running, but still allow it to adjust the frequency of the system clock.
+
*-u* _user_::
This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive (default _@DEFAULT_USER@_).
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
-On Mac OS X, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
+On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
@@ -134,7 +142,7 @@ killed even in normal operation.
*-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
-specified priority (which must be between 0 and 100). On Mac OS X, this option
+specified priority (which must be between 0 and 100). On macOS, this option
must have either a value of 0 (the default) to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
support this option.
@@ -161,4 +169,4 @@ https://chrony.tuxfamily.org/.
== AUTHORS
-chrony was written by Richard Curnow, Miroslav Lichvar and others.
+chrony was written by Richard Curnow, Miroslav Lichvar, and others.
diff --git a/doc/chronyd.man.in b/doc/chronyd.man.in
index 3b8d74a..ef3fa8c 100644
--- a/doc/chronyd.man.in
+++ b/doc/chronyd.man.in
@@ -2,12 +2,12 @@
.\" Title: chronyd
.\" Author: [see the "AUTHORS" section]
.\" Generator: Asciidoctor 1.5.4
-.\" Date: 2016-11-21
+.\" Date: 2016-12-09
.\" Manual: System Administration
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYD" "8" "2016-11-21" "chrony @CHRONY_VERSION@" "System Administration"
+.TH "CHRONYD" "8" "2016-12-09" "chrony @CHRONY_VERSION@" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -83,14 +83,15 @@ corrections of the clock.
.sp
\fB\-r\fP
.RS 4
-This option will reload sample histories for each of the servers and refclocks
-being used. These histories are created by using the
-\fBdump\fP command in \fBchronyc\fP, or by setting the
-\fBdumponexit\fP directive in the configuration
-file. This option is useful if you want to stop and restart \fBchronyd\fP briefly
-for any reason, e.g. to install a new version. However, it should be used only
-on systems where the kernel can maintain clock compensation whilst not under
-\fBchronyd\fP\(cqs control (i.e. Linux, FreeBSD, NetBSD and Solaris).
+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 \fBdump\fP command in
+\fBchronyc\fP, or by setting the \fBdumponexit\fP
+directive in the configuration file. This option is useful if you want to stop
+and restart \fBchronyd\fP briefly for any reason, e.g. to install a new version.
+However, it should be used only on systems where the kernel can maintain clock
+compensation whilst not under \fBchronyd\fP\(cqs control (i.e. Linux, FreeBSD, NetBSD
+and Solaris).
.RE
.sp
\fB\-R\fP
@@ -122,6 +123,15 @@ when \fBchronyd\fP was previously stopped. This is useful on computers that have
RTC or the RTC is broken (e.g. it has no battery).
.RE
.sp
+\fB\-t\fP \fItimeout\fP
+.RS 4
+This option sets a timeout (in seconds) after which \fBchronyd\fP will exit. If the
+clock is not synchronised, it will exit with a non\-zero status. This is useful
+with the \fB\-q\fP or \fB\-Q\fP option to shorten the maximum time waiting for
+measurements, or with the \fB\-r\fP option to limit the time when \fBchronyd\fP is
+running, but still allow it to adjust the frequency of the system clock.
+.RE
+.sp
\fB\-u\fP \fIuser\fP
.RS 4
This option sets the name of the system user to which \fBchronyd\fP will switch
@@ -129,7 +139,7 @@ after start in order to drop root privileges. It overrides the
\fBuser\fP directive (default \fI@DEFAULT_USER@\fP).
.sp
On Linux, \fBchronyd\fP needs to be compiled with support for the \fBlibcap\fP library.
-On Mac OS X, FreeBSD, NetBSD and Solaris \fBchronyd\fP forks into two processes.
+On macOS, FreeBSD, NetBSD and Solaris \fBchronyd\fP forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
.RE
@@ -152,7 +162,7 @@ killed even in normal operation.
\fB\-P\fP \fIpriority\fP
.RS 4
On Linux, this option will select the SCHED_FIFO real\-time scheduler at the
-specified priority (which must be between 0 and 100). On Mac OS X, this option
+specified priority (which must be between 0 and 100). On macOS, this option
must have either a value of 0 (the default) to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
support this option.
@@ -180,4 +190,4 @@ For instructions on how to report bugs, please visit
.URL "https://chrony.tuxfamily.org/" "" "."
.SH "AUTHORS"
.sp
-chrony was written by Richard Curnow, Miroslav Lichvar and others. \ No newline at end of file
+chrony was written by Richard Curnow, Miroslav Lichvar, and others. \ No newline at end of file
diff --git a/doc/faq.adoc b/doc/faq.adoc
index 01076ef..35f0f38 100644
--- a/doc/faq.adoc
+++ b/doc/faq.adoc
@@ -61,8 +61,8 @@ that.
In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialized on the
next boot from the RTC, the `rtcsync` directive enables a mode in which the
-system time is periodically copied to the RTC. It is supported on Linux and Mac
-OS X.
+system time is periodically copied to the RTC. It is supported on Linux and
+macOS.
If you want to use public NTP servers from the
http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
@@ -148,14 +148,19 @@ network. It's better to use more than one server, three or four is usually
recommended as the minimum, so `chronyd` can detect servers that serve false
time and combine measurements from multiple sources.
+If you have a network card with hardware timestamping supported on Linux, it
+can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It
+should make local receive and transmit timestamps of NTP packets much more
+accurate.
+
There are also useful options which can be set in the `server` directive, they
-are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio` and
-`maxdelaydevratio`.
+are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`,
+`maxdelaydevratio`, and `xleave`.
The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their
default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
-`maxpoll` and 6 (samples) for `polltarget`. The default values should be used
+`maxpoll` and 8 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers or if have
permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system
@@ -189,6 +194,16 @@ with local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
+If your server supports the interleaved mode, the `xleave` option should be
+added to the `server` directive in order to receive server's more accurate
+hardware or kernel transmit timestamps. When combined with local hardware
+timestamping, a sub-microsecond accuracy may be possible. An example could be
+
+----
+server ntp.local minpoll 2 maxpoll 2 xleave
+hwtimestamp eth0
+----
+
=== What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the
@@ -287,12 +302,15 @@ authentication (`commandkey` directive).
=== Why does `chronyc tracking` always print an IPv4 address as reference ID?
-The reference ID is a 32-bit value and is always printed in quad-dotted
-notation, even if the reference source doesn't have an IPv4 address. For IPv4
-addresses, the reference ID is equal to the address, but for IPv6 addresses it
-is the first 32 bits of the MD5 sum of the address. For reference clocks, the
-reference ID is the value specified with the `refid` option in the `refclock`
-directive.
+The reference ID is a 32-bit value and in versions before 3.0 it was printed in
+quad-dotted notation, even if the reference source did not actually have an
+IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
+for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
+reference clocks, the reference ID is the value specified with the `refid`
+option in the `refclock` directive.
+
+Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
+confusion with IPv4 addresses.
If you need to get the IP address of the current reference source, use the `-n`
option to disable resolving of IP addresses and read the second field (printed
@@ -373,7 +391,8 @@ to serve time to clients in the network which support the broadcast client mode
=== Can `chronyd` keep the system clock a fixed offset away from real time?
-This is not possible as the program currently stands.
+Yes. Starting from version 3.0, an offset can be specified by the `offset`
+option for all time sources in the _chrony.conf_ file.
=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
diff --git a/examples/chrony.spec b/examples/chrony.spec
index 0a009ec..ec9f77b 100644
--- a/examples/chrony.spec
+++ b/examples/chrony.spec
@@ -1,4 +1,4 @@
-%global chrony_version 2.4.1
+%global chrony_version 3.0-pre1
%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
%endif
diff --git a/hwclock.c b/hwclock.c
new file mode 100644
index 0000000..5ddd5b5
--- /dev/null
+++ b/hwclock.c
@@ -0,0 +1,204 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Tracking of hardware clocks (e.g. RTC, PHC)
+ */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "array.h"
+#include "hwclock.h"
+#include "local.h"
+#include "logging.h"
+#include "memory.h"
+#include "regress.h"
+#include "util.h"
+
+/* Maximum number of samples per clock */
+#define MAX_SAMPLES 16
+
+/* Minimum interval between samples (in seconds) */
+#define MIN_SAMPLE_SEPARATION 1.0
+
+struct HCL_Instance_Record {
+ /* HW and local reference timestamp */
+ struct timespec hw_ref;
+ struct timespec local_ref;
+
+ /* Samples stored as intervals (uncorrected for frequency error)
+ relative to local_ref and hw_ref */
+ double x_data[MAX_SAMPLES];
+ double y_data[MAX_SAMPLES];
+
+ /* Number of samples */
+ int n_samples;
+
+ /* Flag indicating the offset and frequency values are valid */
+ int valid_coefs;
+
+ /* Estimated offset and frequency of HW clock relative to local clock */
+ double offset;
+ double frequency;
+};
+
+/* ================================================== */
+
+static void
+handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
+ double doffset, LCL_ChangeType change_type, void *anything)
+{
+ HCL_Instance clock;
+ double delta;
+
+ clock = anything;
+
+ if (clock->n_samples)
+ UTI_AdjustTimespec(&clock->local_ref, cooked, &clock->local_ref, &delta, dfreq, doffset);
+ if (clock->valid_coefs)
+ clock->frequency /= 1.0 - dfreq;
+}
+
+/* ================================================== */
+
+HCL_Instance
+HCL_CreateInstance(void)
+{
+ HCL_Instance clock;
+
+ clock = MallocNew(struct HCL_Instance_Record);
+ clock->x_data[MAX_SAMPLES - 1] = 0.0;
+ clock->y_data[MAX_SAMPLES - 1] = 0.0;
+ clock->n_samples = 0;
+ clock->valid_coefs = 0;
+
+ LCL_AddParameterChangeHandler(handle_slew, clock);
+
+ return clock;
+}
+
+/* ================================================== */
+
+void HCL_DestroyInstance(HCL_Instance clock)
+{
+ LCL_RemoveParameterChangeHandler(handle_slew, clock);
+ Free(clock);
+}
+
+/* ================================================== */
+
+int
+HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
+{
+ if (!clock->n_samples ||
+ fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= MIN_SAMPLE_SEPARATION)
+ return 1;
+
+ return 0;
+}
+
+/* ================================================== */
+
+void
+HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
+ struct timespec *local_ts, double err)
+{
+ double hw_delta, local_delta, local_freq, raw_freq;
+ int i, n_runs, best_start;
+
+ local_freq = 1.0 - LCL_ReadAbsoluteFrequency() / 1.0e6;
+
+ /* Shift old samples */
+ if (clock->n_samples) {
+ if (clock->n_samples >= MAX_SAMPLES)
+ clock->n_samples--;
+
+ hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
+ local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
+
+ if (hw_delta <= 0.0 || local_delta < MIN_SAMPLE_SEPARATION / 2.0) {
+ clock->n_samples = 0;
+ DEBUG_LOG(LOGF_HwClocks, "HW clock reset interval=%f", local_delta);
+ }
+
+ for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
+ clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
+ clock->x_data[i - 1] = clock->x_data[i] - local_delta;
+ }
+ }
+
+ clock->n_samples++;
+ clock->hw_ref = *hw_ts;
+ clock->local_ref = *local_ts;
+
+ /* Get new coefficients */
+ clock->valid_coefs =
+ RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
+ clock->y_data + MAX_SAMPLES - clock->n_samples,
+ clock->n_samples, 1.0e-9, &clock->offset, &raw_freq,
+ &n_runs, &best_start);
+
+ if (!clock->valid_coefs) {
+ DEBUG_LOG(LOGF_HwClocks, "HW clock needs more samples");
+ return;
+ }
+
+ clock->frequency = raw_freq / local_freq;
+
+ /* Drop unneeded samples */
+ clock->n_samples -= best_start;
+
+ /* If the fit doesn't cross the error interval of the last sample, throw away
+ all previous samples and keep only the frequency estimate */
+ if (fabs(clock->offset) > err) {
+ DEBUG_LOG(LOGF_HwClocks, "HW clock reset offset=%e", clock->offset);
+ clock->offset = 0.0;
+ clock->n_samples = 1;
+ }
+
+ DEBUG_LOG(LOGF_HwClocks, "HW clock samples=%d offset=%e freq=%.9e raw_freq=%.9e err=%e ref_diff=%e",
+ clock->n_samples, clock->offset, clock->frequency, raw_freq, err,
+ UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
+}
+
+/* ================================================== */
+
+int
+HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked, double *err)
+{
+ double offset, elapsed;
+
+ if (!clock->valid_coefs)
+ return 0;
+
+ elapsed = UTI_DiffTimespecsToDouble(raw, &clock->hw_ref);
+ offset = clock->offset + elapsed / clock->frequency;
+ UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
+
+ /* Estimation of the error is not implemented yet */
+ if (err)
+ *err = 0.0;
+
+ return 1;
+}
diff --git a/hwclock.h b/hwclock.h
new file mode 100644
index 0000000..11a79b0
--- /dev/null
+++ b/hwclock.h
@@ -0,0 +1,48 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header for tracking of hardware clocks */
+
+#ifndef GOT_HWCLOCK_H
+#define GOT_HWCLOCK_H
+
+typedef struct HCL_Instance_Record *HCL_Instance;
+
+/* Create a new HW clock instance */
+extern HCL_Instance HCL_CreateInstance(void);
+
+/* Destroy a HW clock instance */
+extern void HCL_DestroyInstance(HCL_Instance clock);
+
+/* Check if a new sample should be accumulated at this time */
+extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
+
+/* Accumulate a new sample */
+extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
+ struct timespec *local_ts, double err);
+
+/* Convert raw hardware time to cooked local time */
+extern int HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
+ double *err);
+
+#endif
diff --git a/keys.c b/keys.c
index 2df93b4..09b27bc 100644
--- a/keys.c
+++ b/keys.c
@@ -103,9 +103,9 @@ static int
determine_hash_delay(uint32_t key_id)
{
NTP_Packet pkt;
- struct timeval before, after;
- unsigned long usecs, min_usecs=0;
- int i;
+ struct timespec before, after;
+ double diff, min_diff;
+ int i, nsecs;
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
@@ -113,19 +113,49 @@ determine_hash_delay(uint32_t key_id)
(unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
LCL_ReadRawTime(&after);
- usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec);
+ diff = UTI_DiffTimespecsToDouble(&after, &before);
- if (i == 0 || usecs < min_usecs) {
- min_usecs = usecs;
- }
+ if (i == 0 || min_diff > diff)
+ min_diff = diff;
}
/* Add on a bit extra to allow for copying, conversions etc */
- min_usecs += min_usecs >> 4;
+ nsecs = 1.0625e9 * min_diff;
- DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %ld useconds", key_id, min_usecs);
+ DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
- return min_usecs;
+ return nsecs;
+}
+
+/* ================================================== */
+/* Decode password encoded in ASCII or HEX */
+
+static int
+decode_password(char *key)
+{
+ int i, j, len = strlen(key);
+ char buf[3], *p;
+
+ if (!strncmp(key, "ASCII:", 6)) {
+ memmove(key, key + 6, len - 6);
+ return len - 6;
+ } else if (!strncmp(key, "HEX:", 4)) {
+ if ((len - 4) % 2)
+ return 0;
+
+ for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
+ buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
+ key[i] = strtol(buf, &p, 16);
+
+ if (p != buf + 2)
+ return 0;
+ }
+
+ return i;
+ } else {
+ /* assume ASCII */
+ return len;
+ }
}
/* ================================================== */
@@ -192,7 +222,7 @@ KEY_Reload(void)
continue;
}
- key.len = UTI_DecodePasswordFromText(keyval);
+ key.len = decode_password(keyval);
if (!key.len) {
LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id);
continue;
@@ -293,6 +323,22 @@ KEY_GetAuthDelay(uint32_t key_id)
/* ================================================== */
int
+KEY_GetAuthLength(uint32_t key_id)
+{
+ unsigned char buf[MAX_HASH_LENGTH];
+ Key *key;
+
+ key = get_key_by_id(key_id);
+
+ if (!key)
+ return 0;
+
+ return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
+}
+
+/* ================================================== */
+
+int
KEY_CheckKeyLength(uint32_t key_id)
{
Key *key;
@@ -307,6 +353,31 @@ KEY_CheckKeyLength(uint32_t key_id)
/* ================================================== */
+static int
+generate_ntp_auth(int hash_id, const unsigned char *key, int key_len,
+ const unsigned char *data, int data_len,
+ unsigned char *auth, int auth_len)
+{
+ return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
+}
+
+/* ================================================== */
+
+static int
+check_ntp_auth(int hash_id, const unsigned char *key, int key_len,
+ const unsigned char *data, int data_len,
+ const unsigned char *auth, int auth_len, int trunc_len)
+{
+ unsigned char buf[MAX_HASH_LENGTH];
+ int hash_len;
+
+ hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
+
+ return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
+}
+
+/* ================================================== */
+
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
@@ -318,15 +389,15 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
- return UTI_GenerateNTPAuth(key->hash_id, (unsigned char *)key->val,
- key->len, data, data_len, auth, auth_len);
+ return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
+ data, data_len, auth, auth_len);
}
/* ================================================== */
int
KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
- const unsigned char *auth, int auth_len)
+ const unsigned char *auth, int auth_len, int trunc_len)
{
Key *key;
@@ -335,6 +406,6 @@ KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
- return UTI_CheckNTPAuth(key->hash_id, (unsigned char *)key->val,
- key->len, data, data_len, auth, auth_len);
+ return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
+ data, data_len, auth, auth_len, trunc_len);
}
diff --git a/keys.h b/keys.h
index 65536cf..82f3a26 100644
--- a/keys.h
+++ b/keys.h
@@ -37,11 +37,12 @@ extern void KEY_Reload(void);
extern int KEY_GetKey(uint32_t key_id, char **key, int *len);
extern int KEY_KeyKnown(uint32_t key_id);
extern int KEY_GetAuthDelay(uint32_t key_id);
+extern int KEY_GetAuthLength(uint32_t key_id);
extern int KEY_CheckKeyLength(uint32_t key_id);
extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data,
int data_len, unsigned char *auth, int auth_len);
-extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data,
- int data_len, const unsigned char *auth, int auth_len);
+extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
+ const unsigned char *auth, int auth_len, int trunc_len);
#endif /* GOT_KEYS_H */
diff --git a/local.c b/local.c
index 2ec173e..82f8fb0 100644
--- a/local.c
+++ b/local.c
@@ -106,37 +106,42 @@ static double max_clock_error;
under 1s of busy waiting. */
#define NITERS 100
+#define NSEC_PER_SEC 1000000000
+
static void
calculate_sys_precision(void)
{
- struct timeval tv, old_tv;
- int dusec, best_dusec;
- int iters;
+ struct timespec ts, old_ts;
+ int iters, diff, best;
+
+ LCL_ReadRawTime(&old_ts);
- gettimeofday(&old_tv, NULL);
- best_dusec = 1000000; /* Assume we must be better than a second */
+ /* Assume we must be better than a second */
+ best = NSEC_PER_SEC;
iters = 0;
+
do {
- gettimeofday(&tv, NULL);
- dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
- old_tv = tv;
- if (dusec > 0) {
- if (dusec < best_dusec) {
- best_dusec = dusec;
- }
+ LCL_ReadRawTime(&ts);
+
+ diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
+
+ old_ts = ts;
+ if (diff > 0) {
+ if (diff < best)
+ best = diff;
iters++;
}
} while (iters < NITERS);
- assert(best_dusec > 0);
+ assert(best > 0);
- precision_quantum = best_dusec * 1.0e-6;
+ precision_quantum = 1.0e-9 * best;
/* Get rounded log2 value of the measured precision */
precision_log = 0;
- while (best_dusec < 707107) {
+ while (best < 707106781) {
precision_log--;
- best_dusec *= 2;
+ best *= 2;
}
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
@@ -278,7 +283,7 @@ LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
/* ================================================== */
static void
-invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked,
+invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
double dfreq, double doffset,
LCL_ChangeType change_type)
{
@@ -345,23 +350,29 @@ void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void
}
/* ================================================== */
-/* At the moment, this is just gettimeofday(), because
- I can't think of a Unix system where it would not be */
void
-LCL_ReadRawTime(struct timeval *result)
+LCL_ReadRawTime(struct timespec *ts)
{
- if (gettimeofday(result, NULL) < 0) {
- LOG_FATAL(LOGF_Local, "gettimeofday() failed");
- }
+#if HAVE_CLOCK_GETTIME
+ if (clock_gettime(CLOCK_REALTIME, ts) < 0)
+ LOG_FATAL(LOGF_Local, "clock_gettime() failed : %s", strerror(errno));
+#else
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) < 0)
+ LOG_FATAL(LOGF_Local, "gettimeofday() failed : %s", strerror(errno));
+
+ UTI_TimevalToTimespec(&tv, ts);
+#endif
}
/* ================================================== */
void
-LCL_ReadCookedTime(struct timeval *result, double *err)
+LCL_ReadCookedTime(struct timespec *result, double *err)
{
- struct timeval raw;
+ struct timespec raw;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, result, err);
@@ -370,18 +381,18 @@ LCL_ReadCookedTime(struct timeval *result, double *err)
/* ================================================== */
void
-LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err)
+LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
{
double correction;
LCL_GetOffsetCorrection(raw, &correction, err);
- UTI_AddDoubleToTimeval(raw, correction, cooked);
+ UTI_AddDoubleToTimespec(raw, correction, cooked);
}
/* ================================================== */
void
-LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err)
+LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
{
/* Call system specific driver to get correction */
(*drv_offset_convert)(raw, correction, err);
@@ -421,7 +432,7 @@ clamp_freq(double freq)
/* ================================================== */
static int
-check_offset(struct timeval *now, double offset)
+check_offset(struct timespec *now, double offset)
{
/* Check if the time will be still sane with accumulated offset */
if (UTI_IsTimeOffsetSane(now, -offset))
@@ -439,7 +450,7 @@ check_offset(struct timeval *now, double offset)
void
LCL_SetAbsoluteFrequency(double afreq_ppm)
{
- struct timeval raw, cooked;
+ struct timespec raw, cooked;
double dfreq;
afreq_ppm = clamp_freq(afreq_ppm);
@@ -470,7 +481,7 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
void
LCL_AccumulateDeltaFrequency(double dfreq)
{
- struct timeval raw, cooked;
+ struct timespec raw, cooked;
double old_freq_ppm;
old_freq_ppm = current_freq_ppm;
@@ -499,7 +510,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
void
LCL_AccumulateOffset(double offset, double corr_rate)
{
- struct timeval raw, cooked;
+ struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */
@@ -521,7 +532,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
int
LCL_ApplyStepOffset(double offset)
{
- struct timeval raw, cooked;
+ struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */
@@ -549,7 +560,7 @@ LCL_ApplyStepOffset(double offset)
/* ================================================== */
void
-LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
+LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion)
{
/* Dispatch to all handlers */
@@ -563,7 +574,7 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
void
LCL_NotifyLeap(int leap)
{
- struct timeval raw, cooked;
+ struct timespec raw, cooked;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
@@ -580,7 +591,7 @@ LCL_NotifyLeap(int leap)
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
- struct timeval raw, cooked;
+ struct timespec raw, cooked;
double old_freq_ppm;
LCL_ReadRawTime(&raw);
@@ -657,7 +668,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
int
LCL_MakeStep(void)
{
- struct timeval raw;
+ struct timespec raw;
double correction;
LCL_ReadRawTime(&raw);
diff --git a/local.h b/local.h
index 0dfdd73..618a0bd 100644
--- a/local.h
+++ b/local.h
@@ -31,9 +31,8 @@
#include "sysincl.h"
-/* Read the system clock. This is analogous to gettimeofday(),
- but with the timezone information ignored */
-extern void LCL_ReadRawTime(struct timeval *);
+/* Read the system clock */
+extern void LCL_ReadRawTime(struct timespec *ts);
/* Read the system clock, corrected according to all accumulated
drifts and uncompensated offsets.
@@ -44,15 +43,15 @@ extern void LCL_ReadRawTime(struct timeval *);
adjtime()-like interface to correct offsets, and to adjust the
frequency), we must correct the raw time to get this value */
-extern void LCL_ReadCookedTime(struct timeval *t, double *err);
+extern void LCL_ReadCookedTime(struct timespec *ts, double *err);
/* Convert raw time to cooked. */
-extern void LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err);
+extern void LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err);
/* Read the current offset between the system clock and true time
(i.e. 'cooked' - 'raw') (in seconds). */
-extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err);
+extern void LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err);
/* Type of routines that may be invoked as callbacks when there is a
change to the frequency or offset.
@@ -79,7 +78,7 @@ typedef enum {
} LCL_ChangeType;
typedef void (*LCL_ParameterChangeHandler)
- (struct timeval *raw, struct timeval *cooked,
+ (struct timespec *raw, struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -163,7 +162,7 @@ extern int LCL_ApplyStepOffset(double offset);
/* Routine to invoke notify handlers on an unexpected time jump
in system clock */
-extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
+extern void LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion);
/* Routine to invoke notify handlers on leap second when the system clock
diff --git a/localp.h b/localp.h
index 5f99309..fbbeb3e 100644
--- a/localp.h
+++ b/localp.h
@@ -52,7 +52,7 @@ typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
/* System driver to convert a raw time to an adjusted (cooked) time.
The number of seconds returned in 'corr' have to be added to the
raw time to get the corrected time */
-typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr, double *err);
+typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err);
/* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap);
diff --git a/logging.c b/logging.c
index 7eb680d..e09203a 100644
--- a/logging.c
+++ b/logging.c
@@ -238,12 +238,18 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return;
if (!logfiles[id].file) {
- char filename[512];
+ char filename[512], *logdir = CNF_GetLogDir();
+
+ if (logdir[0] == '\0') {
+ LOG(LOGS_WARN, LOGF_Logging, "logdir not specified");
+ logfiles[id].name = NULL;
+ return;
+ }
if (snprintf(filename, sizeof(filename), "%s/%s.log",
- CNF_GetLogDir(), logfiles[id].name) >= sizeof(filename) ||
+ logdir, logfiles[id].name) >= sizeof (filename) ||
!(logfiles[id].file = fopen(filename, "a"))) {
- LOG(LOGS_WARN, LOGF_Refclock, "Couldn't open logfile %s for update", filename);
+ LOG(LOGS_WARN, LOGF_Logging, "Could not open log file %s", filename);
logfiles[id].name = NULL;
return;
}
diff --git a/logging.h b/logging.h
index 0ac8e7a..cc8beee 100644
--- a/logging.h
+++ b/logging.h
@@ -47,10 +47,10 @@ extern int log_debug_enabled;
#if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \
- LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__);
+ LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#else
#define LOG_MESSAGE(severity, facility, ...) \
- LOG_Message(severity, __VA_ARGS__);
+ LOG_Message(severity, __VA_ARGS__)
#endif
#define DEBUG_LOG(facility, ...) \
@@ -82,7 +82,9 @@ typedef enum {
typedef enum {
LOGF_Reference,
LOGF_NtpIO,
+ LOGF_NtpIOLinux,
LOGF_NtpCore,
+ LOGF_NtpSignd,
LOGF_NtpSources,
LOGF_Scheduler,
LOGF_SourceStats,
@@ -114,6 +116,7 @@ typedef enum {
LOGF_TempComp,
LOGF_RtcLinux,
LOGF_Refclock,
+ LOGF_HwClocks,
LOGF_Smooth,
} LOG_Facility;
diff --git a/main.c b/main.c
index 214934e..aaa8744 100644
--- a/main.c
+++ b/main.c
@@ -35,6 +35,7 @@
#include "local.h"
#include "sys.h"
#include "ntp_io.h"
+#include "ntp_signd.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "sources.h"
@@ -107,6 +108,7 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
+ NSD_Finalise();
NSR_Finalise();
SST_Finalise();
NCR_Finalise();
@@ -144,6 +146,16 @@ signal_cleanup(int x)
/* ================================================== */
static void
+quit_timeout(void *arg)
+{
+ /* Return with non-zero status if the clock is not synchronised */
+ exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
+ SCH_QuitProgram();
+}
+
+/* ================================================== */
+
+static void
ntp_source_resolving_end(void)
{
NSR_SetSourceResolvingEndHandler(NULL);
@@ -156,6 +168,7 @@ ntp_source_resolving_end(void)
SRC_ReloadSources();
}
+ SRC_RemoveDumpFiles();
RTC_StartMeasurements();
RCL_StartRefclocks();
NSR_StartSources();
@@ -294,14 +307,14 @@ go_daemon(void)
/* Create pipe which will the daemon use to notify the grandparent
when it's initialised or send an error message */
if (pipe(pipefd)) {
- LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno));
+ LOG_FATAL(LOGF_Main, "Could not detach, pipe failed : %s", strerror(errno));
}
/* Does this preserve existing signal handlers? */
pid = fork();
if (pid < 0) {
- LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
+ LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) {
/* In the 'grandparent' */
char message[1024];
@@ -326,7 +339,7 @@ go_daemon(void)
pid = fork();
if (pid < 0) {
- LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
+ LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) {
exit(0); /* In the 'parent' */
} else {
@@ -334,7 +347,7 @@ go_daemon(void)
/* Change current directory to / */
if (chdir("/") < 0) {
- LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
+ LOG_FATAL(LOGF_Main, "Could not chdir to / : %s", strerror(errno));
}
/* Don't keep stdin/out/err from before. But don't close
@@ -359,7 +372,7 @@ int main
char *user = NULL;
struct passwd *pw;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
- int do_init_rtc = 0, restarted = 0;
+ int do_init_rtc = 0, restarted = 0, timeout = 0;
int other_pid;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int system_log = 1;
@@ -417,12 +430,16 @@ int main
ref_mode = REF_ModePrintOnce;
nofork = 1;
system_log = 0;
+ } else if (!strcmp("-t", *argv)) {
+ ++argv, --argc;
+ if (argc == 0 || sscanf(*argv, "%d", &timeout) != 1 || timeout <= 0)
+ LOG_FATAL(LOGF_Main, "Bad timeout");
} else if (!strcmp("-4", *argv)) {
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
- printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n",
+ printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
return 0;
} else if (*argv[0] == '-') {
@@ -523,6 +540,7 @@ int main
REF_Initialise();
SST_Initialise();
NSR_Initialise();
+ NSD_Initialise();
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();
@@ -545,6 +563,9 @@ int main
REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode);
+ if (timeout)
+ SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
+
if (do_init_rtc) {
RTC_TimeInit(post_init_rtc_hook, NULL);
} else {
diff --git a/manual.c b/manual.c
index da2ed92..4124234 100644
--- a/manual.c
+++ b/manual.c
@@ -47,7 +47,7 @@ static int enabled = 0;
/* More recent samples at highest indices */
typedef struct {
- struct timeval when; /* This is our 'cooked' time */
+ struct timespec when; /* This is our 'cooked' time */
double orig_offset; /*+ Not modified by slew samples */
double offset; /*+ if we are fast of the supplied reference */
double residual; /*+ regression residual (sign convention given by
@@ -64,8 +64,8 @@ static int n_samples;
/* ================================================== */
static void
-slew_samples(struct timeval *raw,
- struct timeval *cooked,
+slew_samples(struct timespec *raw,
+ struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -97,7 +97,7 @@ MNL_Finalise(void)
/* ================================================== */
static void
-estimate_and_set_system(struct timeval *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
+estimate_and_set_system(struct timespec *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
{
double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
double b0, b1;
@@ -110,7 +110,7 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
if (n_samples > 1) {
for (i=0; i<n_samples; i++) {
- UTI_DiffTimevalsToDouble(&agos[i], &samples[n_samples-1].when, &samples[i].when);
+ agos[i] = UTI_DiffTimespecsToDouble(&samples[n_samples - 1].when, &samples[i].when);
offsets[i] = samples[i].offset;
}
@@ -173,9 +173,9 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
/* ================================================== */
int
-MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
+MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
{
- struct timeval now;
+ struct timespec now;
double offset, diff;
int i;
@@ -189,12 +189,12 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
return 0;
if (n_samples) {
- UTI_DiffTimevalsToDouble(&diff, &now, &samples[n_samples - 1].when);
+ diff = UTI_DiffTimespecsToDouble(&now, &samples[n_samples - 1].when);
if (diff < MIN_SAMPLE_SEPARATION)
return 0;
}
- UTI_DiffTimevalsToDouble(&offset, &now, ts);
+ offset = UTI_DiffTimespecsToDouble(&now, ts);
/* Check if buffer full up */
if (n_samples == MAX_SAMPLES) {
@@ -224,8 +224,8 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
/* ================================================== */
static void
-slew_samples(struct timeval *raw,
- struct timeval *cooked,
+slew_samples(struct timespec *raw,
+ struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -239,7 +239,7 @@ slew_samples(struct timeval *raw,
}
for (i=0; i<n_samples; i++) {
- UTI_AdjustTimeval(&samples[i].when, cooked, &samples[i].when, &delta_time,
+ UTI_AdjustTimespec(&samples[i].when, cooked, &samples[i].when, &delta_time,
dfreq, doffset);
samples[i].offset += delta_time;
}
@@ -309,7 +309,7 @@ int
MNL_DeleteSample(int index)
{
int i;
- struct timeval now;
+ struct timespec now;
if ((index < 0) || (index >= n_samples)) {
return 0;
diff --git a/manual.h b/manual.h
index bf0c72a..06a5d02 100644
--- a/manual.h
+++ b/manual.h
@@ -33,7 +33,7 @@
extern void MNL_Initialise(void);
extern void MNL_Finalise(void);
-extern int MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
+extern int MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
extern void MNL_Enable(void);
extern void MNL_Disable(void);
diff --git a/nameserv_async.c b/nameserv_async.c
index 0175cc2..e993235 100644
--- a/nameserv_async.c
+++ b/nameserv_async.c
@@ -72,7 +72,7 @@ start_resolving(void *anything)
/* ================================================== */
static void
-end_resolving(void *anything)
+end_resolving(int fd, int event, void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
int i;
@@ -83,7 +83,7 @@ end_resolving(void *anything)
resolving_threads--;
- SCH_RemoveInputFileHandler(inst->pipe[0]);
+ SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
@@ -120,7 +120,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
}
- SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst);
+ SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst);
}
/* ================================================== */
diff --git a/ntp.h b/ntp.h
index 08a8bf6..e2abd84 100644
--- a/ntp.h
+++ b/ntp.h
@@ -56,6 +56,10 @@ typedef uint32_t NTP_int32;
#define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
+/* The maximum length of MAC in NTPv4 packets which allows deterministic
+ parsing of extension fields (RFC 7822) */
+#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
+
/* Type definition for leap bits */
typedef enum {
LEAP_Normal = 0,
diff --git a/ntp_core.c b/ntp_core.c
index 7942693..9695bd6 100644
--- a/ntp_core.c
+++ b/ntp_core.c
@@ -32,6 +32,7 @@
#include "array.h"
#include "ntp_core.h"
#include "ntp_io.h"
+#include "ntp_signd.h"
#include "memory.h"
#include "sched.h"
#include "reference.h"
@@ -61,6 +62,16 @@ typedef enum {
} OperatingMode;
/* ================================================== */
+/* Enumeration for authentication modes of NTP packets */
+
+typedef enum {
+ AUTH_NONE = 0, /* No authentication */
+ AUTH_SYMMETRIC, /* MAC using symmetric key (RFC 1305, RFC 5905) */
+ AUTH_MSSNTP, /* MS-SNTP authenticator field */
+ AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */
+} AuthenticationMode;
+
+/* ================================================== */
/* Structure used for holding a single peer/server's
protocol machine */
@@ -69,6 +80,7 @@ struct NCR_Instance_Record {
NTP_Local_Address local_addr; /* Local address/socket used to send packets */
NTP_Mode mode; /* The source's NTP mode
(client/server or symmetric active peer) */
+ int interleaved; /* Boolean enabling interleaved NTP mode */
OperatingMode opmode; /* Whether we are sampling this source
or not and in what way */
SCH_TimeoutID rx_timeout_id; /* Timeout ID for latest received response */
@@ -119,12 +131,10 @@ struct NCR_Instance_Record {
double max_delay_dev_ratio; /* Maximum ratio of increase in delay / stddev */
- int do_auth; /* Flag indicating whether we
- authenticate packets we send to
- this machine (if it's serving us or
- the association is symmetric). Note
- : we don't authenticate if we can't
- find the key in our database. */
+ double offset_correction; /* Correction applied to measured offset
+ (e.g. for asymmetry in network delay) */
+
+ AuthenticationMode auth_mode; /* Authentication mode of our requests */
uint32_t auth_key_id; /* The ID of the authentication key to
use. */
@@ -132,10 +142,19 @@ struct NCR_Instance_Record {
receive from this peer */
int tx_count;
- /* Timestamp in tx field of last received packet. We have to
- reproduce this exactly as the orig field or our outgoing
- packet. */
- NTP_int64 remote_orig;
+ /* Flag indicating a valid response was received since last request */
+ int valid_rx;
+
+ /* Flag indicating the timestamps below are from a valid packet and may
+ be used for synchronisation */
+ int valid_timestamps;
+
+ /* Flag indicating the timestamps below were updated since last request */
+ int updated_timestamps;
+
+ /* Receive and transmit timestamps from the last received packet */
+ NTP_int64 remote_ntp_rx;
+ NTP_int64 remote_ntp_tx;
/* Local timestamp when the last packet was received from the
source. We have to be prepared to tinker with this if the local
@@ -145,18 +164,19 @@ struct NCR_Instance_Record {
parameters for the current reference. (It must be stored
relative to local time to permit frequency and offset adjustments
to be made when we trim the local clock). */
- struct timeval local_rx;
+ NTP_int64 local_ntp_rx;
+ NTP_Local_Timestamp local_rx;
/* Local timestamp when we last transmitted a packet to the source.
We store two versions. The first is in NTP format, and is used
to validate the next received packet from the source.
Additionally, this is corrected to bring it into line with the
- current reference. The second is in timeval format, and is kept
+ current reference. The second is in timespec format, and is kept
relative to the local clock. We modify this in accordance with
local clock frequency/offset changes, and use this for computing
statistics about the source when a return packet arrives. */
NTP_int64 local_ntp_tx;
- struct timeval local_tx;
+ NTP_Local_Timestamp local_tx;
/* The instance record in the main source management module. This
performs the statistical analysis on the samples we generate */
@@ -166,6 +186,8 @@ struct NCR_Instance_Record {
int burst_good_samples_to_go;
int burst_total_samples_to_go;
+ /* Report from last valid response */
+ RPT_NTPReport report;
};
typedef struct {
@@ -203,7 +225,7 @@ static ARR_Instance broadcasts;
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
/* Time to wait after sending packet to 'warm up' link */
-#define WARM_UP_DELAY 4.0
+#define WARM_UP_DELAY 2.0
/* Compatible NTP protocol versions */
#define NTP_MAX_COMPAT_VERSION NTP_VERSION
@@ -218,6 +240,9 @@ static ARR_Instance broadcasts;
/* Maximum allowed time for server to process client packet */
#define MAX_SERVER_INTERVAL 4.0
+/* Maximum acceptable delay in transmission for timestamp correction */
+#define MAX_TX_DELAY 1.0
+
/* Minimum and maximum allowed poll interval */
#define MIN_POLL 0
#define MAX_POLL 24
@@ -239,6 +264,10 @@ static int server_sock_fd6;
static ADF_AuthTable access_auth_table;
+/* Characters for printing synchronisation status and timestamping source */
+static const char leap_chars[4] = {'N', '+', '-', '?'};
+static const char tss_chars[3] = {'D', 'K', 'H'};
+
/* ================================================== */
/* Forward prototypes */
@@ -276,30 +305,30 @@ do_size_checks(void)
static void
do_time_checks(void)
{
- struct timeval now;
+ struct timespec now;
time_t warning_advance = 3600 * 24 * 365 * 10; /* 10 years */
#ifdef HAVE_LONG_TIME_T
/* Check that time before NTP_ERA_SPLIT underflows correctly */
- struct timeval tv1 = {NTP_ERA_SPLIT, 1}, tv2 = {NTP_ERA_SPLIT - 1, 1};
- NTP_int64 ntv1, ntv2;
+ struct timespec ts1 = {NTP_ERA_SPLIT, 1}, ts2 = {NTP_ERA_SPLIT - 1, 1};
+ NTP_int64 nts1, nts2;
int r;
- UTI_TimevalToInt64(&tv1, &ntv1, NULL);
- UTI_TimevalToInt64(&tv2, &ntv2, NULL);
- UTI_Int64ToTimeval(&ntv1, &tv1);
- UTI_Int64ToTimeval(&ntv2, &tv2);
+ UTI_TimespecToNtp64(&ts1, &nts1, NULL);
+ UTI_TimespecToNtp64(&ts2, &nts2, NULL);
+ UTI_Ntp64ToTimespec(&nts1, &ts1);
+ UTI_Ntp64ToTimespec(&nts2, &ts2);
- r = tv1.tv_sec == NTP_ERA_SPLIT &&
- tv1.tv_sec + (1ULL << 32) - 1 == tv2.tv_sec;
+ r = ts1.tv_sec == NTP_ERA_SPLIT &&
+ ts1.tv_sec + (1ULL << 32) - 1 == ts2.tv_sec;
assert(r);
LCL_ReadRawTime(&now);
- if (tv2.tv_sec - now.tv_sec < warning_advance)
+ if (ts2.tv_sec - now.tv_sec < warning_advance)
LOG(LOGS_WARN, LOGF_NtpCore, "Assumed NTP time ends at %s!",
- UTI_TimeToLogForm(tv2.tv_sec));
+ UTI_TimeToLogForm(ts2.tv_sec));
#else
LCL_ReadRawTime(&now);
if (now.tv_sec > 0x7fffffff - warning_advance)
@@ -317,7 +346,7 @@ NCR_Initialise(void)
do_time_checks();
logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements",
- " Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp.")
+ " Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp. Refid MTxRx")
: -1;
access_auth_table = ADF_CreateTable();
@@ -376,7 +405,7 @@ static void
start_initial_timeout(NCR_Instance inst)
{
double delay, last_tx;
- struct timeval now;
+ struct timespec now;
if (!inst->tx_timeout_id) {
/* This will be the first transmission after mode change */
@@ -389,7 +418,7 @@ start_initial_timeout(NCR_Instance inst)
the interval between packets at least as long as the current polling
interval */
SCH_GetLastEventTime(&now, NULL, NULL);
- UTI_DiffTimevalsToDouble(&last_tx, &now, &inst->local_tx);
+ last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
if (last_tx < 0.0)
last_tx = 0.0;
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
@@ -460,6 +489,8 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
assert(0);
}
+ result->interleaved = params->interleaved;
+
result->minpoll = params->minpoll;
if (result->minpoll < MIN_POLL)
result->minpoll = SRC_DEFAULT_MINPOLL;
@@ -476,25 +507,26 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->min_stratum = params->min_stratum;
if (result->min_stratum >= NTP_MAX_STRATUM)
result->min_stratum = NTP_MAX_STRATUM - 1;
+
+ /* Presend doesn't work in symmetric mode */
result->presend_minpoll = params->presend_minpoll;
+ if (result->presend_minpoll <= MAX_POLL && result->mode != MODE_CLIENT)
+ result->presend_minpoll = MAX_POLL + 1;
result->max_delay = params->max_delay;
result->max_delay_ratio = params->max_delay_ratio;
result->max_delay_dev_ratio = params->max_delay_dev_ratio;
+ result->offset_correction = params->offset;
result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target;
- result->version = params->version;
- if (result->version < NTP_MIN_COMPAT_VERSION)
- result->version = NTP_MIN_COMPAT_VERSION;
- else if (result->version > NTP_VERSION)
- result->version = NTP_VERSION;
+ result->version = NTP_VERSION;
if (params->authkey == INACTIVE_AUTHKEY) {
- result->do_auth = 0;
+ result->auth_mode = AUTH_NONE;
result->auth_key_id = 0;
} else {
- result->do_auth = 1;
+ result->auth_mode = AUTH_SYMMETRIC;
result->auth_key_id = params->authkey;
if (!KEY_KeyKnown(result->auth_key_id)) {
LOG(LOGS_WARN, LOGF_NtpCore, "Key %"PRIu32" used by source %s is %s",
@@ -505,8 +537,16 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr),
"too short");
}
+
+ /* If the MAC in NTPv4 packets would be truncated, use version 3 by
+ default for compatibility with older chronyd servers */
+ if (KEY_GetAuthLength(result->auth_key_id) + 4 > NTP_MAX_V4_MAC_LENGTH)
+ result->version = 3;
}
+ if (params->version)
+ result->version = CLAMP(NTP_MIN_COMPAT_VERSION, params->version, NTP_VERSION);
+
/* Create a source instance for this NTP source */
result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr),
SRC_NTP, params->sel_options,
@@ -518,8 +558,13 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->tx_suspended = 1;
result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
result->local_poll = result->minpoll;
- result->local_tx.tv_sec = 0;
- result->local_tx.tv_usec = 0;
+ result->poll_score = 0.0;
+ UTI_ZeroTimespec(&result->local_tx.ts);
+ result->local_tx.err = 0.0;
+ result->local_tx.source = NTP_TS_DAEMON;
+ result->burst_good_samples_to_go = 0;
+ result->burst_total_samples_to_go = 0;
+ memset(&result->report, 0, sizeof (result->report));
NCR_ResetInstance(result);
@@ -569,17 +614,26 @@ NCR_ResetInstance(NCR_Instance instance)
instance->tx_count = 0;
instance->presend_done = 0;
- instance->poll_score = 0.0;
instance->remote_poll = 0;
instance->remote_stratum = 0;
- instance->remote_orig.hi = 0;
- instance->remote_orig.lo = 0;
- instance->local_rx.tv_sec = 0;
- instance->local_rx.tv_usec = 0;
- instance->local_ntp_tx.hi = 0;
- instance->local_ntp_tx.lo = 0;
+ instance->valid_rx = 0;
+ instance->valid_timestamps = 0;
+ instance->updated_timestamps = 0;
+ UTI_ZeroNtp64(&instance->remote_ntp_rx);
+ UTI_ZeroNtp64(&instance->remote_ntp_tx);
+ UTI_ZeroNtp64(&instance->local_ntp_rx);
+ UTI_ZeroNtp64(&instance->local_ntp_tx);
+ UTI_ZeroTimespec(&instance->local_rx.ts);
+ instance->local_rx.err = 0.0;
+ instance->local_rx.source = NTP_TS_DAEMON;
+}
+
+/* ================================================== */
+void
+NCR_ResetPoll(NCR_Instance instance)
+{
if (instance->local_poll != instance->minpoll) {
instance->local_poll = instance->minpoll;
@@ -594,9 +648,8 @@ NCR_ResetInstance(NCR_Instance instance)
void
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
{
+ NCR_ResetInstance(inst);
inst->remote_addr = *remote_addr;
- inst->tx_count = 0;
- inst->presend_done = 0;
if (inst->mode == MODE_CLIENT)
close_client_socket(inst);
@@ -657,7 +710,7 @@ get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance)
poll_adj = -shift - inst->poll_score + 0.5;
} else {
- int samples = SRC_Samples(inst->source);
+ int samples = SST_Samples(SRC_GetSourcestats(inst->source));
/* Adjust polling interval so that the number of sourcestats samples
remains close to the target value */
@@ -707,6 +760,8 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
poll_to_use = inst->local_poll;
delay_time = (double) (1UL<<poll_to_use);
+ if (inst->presend_done)
+ delay_time = WARM_UP_DELAY;
break;
@@ -781,42 +836,44 @@ receive_timeout(void *arg)
static int
transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
+ int interleaved, /* Flag enabling interleaved mode */
int my_poll, /* The log2 of the local poll interval */
int version, /* The NTP version to be set in the packet */
- int do_auth, /* Boolean indicating whether to authenticate the packet or not */
+ int auth_mode, /* The authentication mode */
uint32_t key_id, /* The authentication key ID */
- NTP_int64 *orig_ts, /* Originate timestamp (from received packet) */
- struct timeval *local_rx, /* Local time request packet was received */
- struct timeval *local_tx, /* RESULT : Time this reply
- is sent as local time, or
- NULL if don't want to
- know */
- NTP_int64 *local_ntp_tx, /* RESULT : Time reply sent
- as NTP timestamp
- (including adjustment to
- reference), ignored if
- NULL */
+ NTP_int64 *remote_ntp_rx, /* The receive timestamp from received packet */
+ NTP_int64 *remote_ntp_tx, /* The transmit timestamp from received packet */
+ NTP_Local_Timestamp *local_rx, /* The RX time of the received packet */
+ NTP_Local_Timestamp *local_tx, /* The TX time of the previous packet
+ RESULT : TX time of this packet */
+ NTP_int64 *local_ntp_rx, /* RESULT : receive timestamp from this packet */
+ NTP_int64 *local_ntp_tx, /* RESULT : transmit timestamp from this packet */
NTP_Remote_Address *where_to, /* Where to address the reponse to */
NTP_Local_Address *from /* From what address to send it */
)
{
NTP_Packet message;
- int auth_len, length, ret, precision;
- struct timeval local_receive, local_transmit;
+ int auth_len, mac_len, length, ret, precision;
+ struct timespec local_receive, local_transmit;
+ double smooth_offset, local_transmit_err;
NTP_int64 ts_fuzz;
/* Parameters read from reference module */
int are_we_synchronised, our_stratum, smooth_time;
NTP_Leap leap_status;
uint32_t our_ref_id;
- struct timeval our_ref_time;
- double our_root_delay, our_root_dispersion, smooth_offset;
+ struct timespec our_ref_time;
+ double our_root_delay, our_root_dispersion;
/* Don't reply with version higher than ours */
if (version > NTP_VERSION) {
version = NTP_VERSION;
}
+ /* Allow interleaved mode only if there was a prior transmission */
+ if (interleaved && (!local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
+ interleaved = 0;
+
smooth_time = 0;
smooth_offset = 0.0;
@@ -824,8 +881,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
/* Don't reveal local time or state of the clock in client packets */
precision = 32;
leap_status = our_stratum = our_ref_id = 0;
- our_ref_time.tv_sec = our_ref_time.tv_usec = 0;
our_root_delay = our_root_dispersion = 0.0;
+ UTI_ZeroTimespec(&our_ref_time);
} else {
/* This is accurate enough and cheaper than calling LCL_ReadCookedTime.
A more accurate timestamp will be taken later in this function. */
@@ -851,12 +908,12 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
precision = LCL_GetSysPrecisionAsLog();
}
- if (smooth_time) {
+ if (smooth_time && !UTI_IsZeroTimespec(&local_rx->ts)) {
our_ref_id = NTP_REFID_SMOOTH;
- UTI_AddDoubleToTimeval(&our_ref_time, smooth_offset, &our_ref_time);
- UTI_AddDoubleToTimeval(local_rx, smooth_offset, &local_receive);
+ UTI_AddDoubleToTimespec(&our_ref_time, smooth_offset, &our_ref_time);
+ UTI_AddDoubleToTimespec(&local_rx->ts, smooth_offset, &local_receive);
} else {
- local_receive = *local_rx;
+ local_receive = local_rx->ts;
}
/* Generate transmit packet */
@@ -873,78 +930,91 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
/* If we're sending a client mode packet and we aren't synchronized yet,
we might have to set up artificial values for some of these parameters */
- message.root_delay = UTI_DoubleToInt32(our_root_delay);
- message.root_dispersion = UTI_DoubleToInt32(our_root_dispersion);
+ message.root_delay = UTI_DoubleToNtp32(our_root_delay);
+ message.root_dispersion = UTI_DoubleToNtp32(our_root_dispersion);
message.reference_id = htonl(our_ref_id);
/* Now fill in timestamps */
- UTI_TimevalToInt64(&our_ref_time, &message.reference_ts, NULL);
+ UTI_TimespecToNtp64(&our_ref_time, &message.reference_ts, NULL);
/* Originate - this comes from the last packet the source sent us */
- message.originate_ts = *orig_ts;
+ message.originate_ts = interleaved ? *remote_ntp_rx : *remote_ntp_tx;
/* Prepare random bits which will be added to the receive timestamp */
- UTI_GetInt64Fuzz(&ts_fuzz, precision);
+ UTI_GetNtp64Fuzz(&ts_fuzz, precision);
/* Receive - this is when we received the last packet from the source.
This timestamp will have been adjusted so that it will now look to
the source like we have been running on our latest estimate of
frequency all along */
- UTI_TimevalToInt64(&local_receive, &message.receive_ts, &ts_fuzz);
+ UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz);
/* Prepare random bits which will be added to the transmit timestamp. */
- UTI_GetInt64Fuzz(&ts_fuzz, precision);
+ UTI_GetNtp64Fuzz(&ts_fuzz, precision);
/* Transmit - this our local time right now! Also, we might need to
store this for our own use later, next time we receive a message
from the source we're sending to now. */
- LCL_ReadCookedTime(&local_transmit, NULL);
+ LCL_ReadCookedTime(&local_transmit, &local_transmit_err);
if (smooth_time)
- UTI_AddDoubleToTimeval(&local_transmit, smooth_offset, &local_transmit);
+ UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
length = NTP_NORMAL_PACKET_LENGTH;
- /* Authenticate */
- if (do_auth && key_id) {
+ /* Authenticate the packet if needed */
+
+ if (auth_mode == AUTH_SYMMETRIC || auth_mode == AUTH_MSSNTP) {
/* Pre-compensate the transmit time by approx. how long it will
take to generate the authentication data. */
- local_transmit.tv_usec += KEY_GetAuthDelay(key_id);
- UTI_NormaliseTimeval(&local_transmit);
- UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, &ts_fuzz);
-
- auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
- offsetof(NTP_Packet, auth_keyid),
- (unsigned char *)&message.auth_data, sizeof (message.auth_data));
- if (auth_len > 0) {
+ local_transmit.tv_nsec += auth_mode == AUTH_SYMMETRIC ?
+ KEY_GetAuthDelay(key_id) : NSD_GetAuthDelay(key_id);
+ UTI_NormaliseTimespec(&local_transmit);
+ UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
+ &message.transmit_ts, &ts_fuzz);
+
+ if (auth_mode == AUTH_SYMMETRIC) {
+ auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
+ offsetof(NTP_Packet, auth_keyid),
+ (unsigned char *)&message.auth_data,
+ sizeof (message.auth_data));
+ if (!auth_len) {
+ DEBUG_LOG(LOGF_NtpCore, "Could not generate auth data with key %"PRIu32, key_id);
+ return 0;
+ }
+
message.auth_keyid = htonl(key_id);
- length += sizeof (message.auth_keyid) + auth_len;
- } else {
- DEBUG_LOG(LOGF_NtpCore,
- "Could not generate auth data with key %"PRIu32" to send packet",
- key_id);
- return 0;
+ 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;
+ } 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);
}
} else {
- if (do_auth) {
- /* Zero key ID means crypto-NAK, append only the ID without any data */
- message.auth_keyid = 0;
- length += sizeof (message.auth_keyid);
- }
- UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, &ts_fuzz);
+ UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
+ &message.transmit_ts, &ts_fuzz);
}
- ret = NIO_SendPacket(&message, where_to, from, length);
+ ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
if (local_tx) {
- *local_tx = local_transmit;
+ local_tx->ts = local_transmit;
+ local_tx->err = local_transmit_err;
+ local_tx->source = NTP_TS_DAEMON;
}
- if (local_ntp_tx) {
+ if (local_ntp_rx)
+ *local_ntp_rx = message.receive_ts;
+ if (local_ntp_tx)
*local_ntp_tx = message.transmit_ts;
- }
return ret;
}
@@ -998,38 +1068,29 @@ transmit_timeout(void *arg)
/* Check whether we need to 'warm up' the link to the other end by
sending an NTP exchange to ensure both ends' ARP caches are
- primed. On loaded systems this might also help ensure that bits
- of the program are paged in properly before we start. */
-
- if ((inst->presend_minpoll > 0) &&
- (inst->presend_minpoll <= inst->local_poll) &&
- !inst->presend_done) {
-
- /* Send a client packet, don't store the local tx values
- as the reply will be ignored */
- transmit_packet(MODE_CLIENT, inst->local_poll, inst->version, 0, 0,
- &inst->remote_orig, &inst->local_rx, NULL, NULL,
- &inst->remote_addr, &local_addr);
-
- inst->presend_done = 1;
-
- /* Requeue timeout */
- restart_timeout(inst, WARM_UP_DELAY);
-
- return;
+ primed or whether we need to send two packets first to ensure a
+ server in the interleaved mode has a fresh timestamp for us. */
+ if (inst->presend_minpoll <= inst->local_poll && !inst->presend_done &&
+ !inst->burst_total_samples_to_go) {
+ inst->presend_done = inst->interleaved ? 2 : 1;
+ } else if (inst->presend_done > 0) {
+ inst->presend_done--;
}
- inst->presend_done = 0; /* Reset for next time */
-
- sent = transmit_packet(inst->mode, inst->local_poll,
+ sent = transmit_packet(inst->mode, inst->interleaved, inst->local_poll,
inst->version,
- inst->do_auth, inst->auth_key_id,
- &inst->remote_orig,
- &inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
+ inst->auth_mode, inst->auth_key_id,
+ &inst->remote_ntp_rx, &inst->remote_ntp_tx,
+ &inst->local_rx, &inst->local_tx,
+ &inst->local_ntp_rx, &inst->local_ntp_tx,
&inst->remote_addr,
&local_addr);
++inst->tx_count;
+ inst->valid_rx = 0;
+ inst->updated_timestamps = 0;
+ if (sent)
+ inst->report.total_tx_count++;
/* If the source loses connectivity and our packets are still being sent,
back off the sampling rate to reduce the network traffic. If it's the
@@ -1098,38 +1159,64 @@ check_packet_format(NTP_Packet *message, int length)
/* ================================================== */
static int
-check_packet_auth(NTP_Packet *pkt, int length, int *has_auth, uint32_t *key_id)
+is_zero_data(unsigned char *data, int length)
{
- int i, remainder, ext_length;
+ int i;
+
+ for (i = 0; i < length; i++)
+ if (data[i])
+ return 0;
+ return 1;
+}
+
+/* ================================================== */
+
+static int
+check_packet_auth(NTP_Packet *pkt, int length,
+ AuthenticationMode *auth_mode, uint32_t *key_id)
+{
+ int i, version, remainder, ext_length, max_mac_length;
unsigned char *data;
uint32_t id;
/* Go through extension fields and see if there is a valid MAC */
+ version = NTP_LVM_TO_VERSION(pkt->lvm);
i = NTP_NORMAL_PACKET_LENGTH;
data = (void *)pkt;
while (1) {
remainder = length - i;
- /* Check if the remaining data is a valid MAC. This needs to be done
- before trying to parse it as an extension field, because we support
- MACs longer than the shortest valid extension field. */
- if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_MAC_LENGTH) {
+ /* Check if the remaining data is a valid MAC. There is a limit on MAC
+ length in NTPv4 packets to allow deterministic parsing of extension
+ fields (RFC 7822), but we need to support longer MACs to not break
+ compatibility with older chrony clients. This needs to be done before
+ trying to parse the data as an extension field. */
+
+ max_mac_length = version == 4 && remainder <= NTP_MAX_V4_MAC_LENGTH ?
+ NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH;
+
+ if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= max_mac_length) {
id = ntohl(*(uint32_t *)(data + i));
if (KEY_CheckAuth(id, (void *)pkt, i, (void *)(data + i + 4),
- remainder - 4)) {
- if (key_id)
- *key_id = id;
- if (has_auth)
- *has_auth = 1;
+ remainder - 4, max_mac_length - 4)) {
+ *auth_mode = AUTH_SYMMETRIC;
+ *key_id = id;
+
+ /* If it's an NTPv4 packet with long MAC and no extension fields,
+ rewrite the version in the packet to respond with long MAC too */
+ if (version == 4 && NTP_NORMAL_PACKET_LENGTH + remainder == length &&
+ remainder > NTP_MAX_V4_MAC_LENGTH)
+ pkt->lvm = NTP_LVM(NTP_LVM_TO_LEAP(pkt->lvm), 3, NTP_LVM_TO_MODE(pkt->lvm));
+
return 1;
}
}
- /* Check if this is a valid field extension. They consist of 16-bit type,
- 16-bit length of the whole field aligned to 32 bits and data. */
- if (remainder >= NTP_MIN_EXTENSION_LENGTH) {
+ /* Check if this is a valid NTPv4 extension field and skip it. It should
+ have a 16-bit type, 16-bit length, and data padded to 32 bits. */
+ if (version == 4 && remainder >= NTP_MIN_EXTENSION_LENGTH) {
ext_length = ntohs(*(uint16_t *)(data + i + 2));
if (ext_length >= NTP_MIN_EXTENSION_LENGTH &&
ext_length <= remainder && ext_length % 4 == 0) {
@@ -1144,9 +1231,23 @@ check_packet_auth(NTP_Packet *pkt, int length, int *has_auth, uint32_t *key_id)
/* This is not 100% reliable as a MAC could fail to authenticate and could
pass as an extension field, leaving reminder smaller than the minimum MAC
- length. Not a big problem, at worst we won't reply with a crypto-NAK. */
- if (has_auth)
- *has_auth = remainder >= NTP_MIN_MAC_LENGTH;
+ length */
+ if (remainder >= NTP_MIN_MAC_LENGTH) {
+ *auth_mode = AUTH_SYMMETRIC;
+ *key_id = ntohl(*(uint32_t *)(data + i));
+
+ /* Check if it is an MS-SNTP authenticator field or extended authenticator
+ field with zeroes as digest */
+ if (version == 3 && *key_id) {
+ if (remainder == 20 && is_zero_data(data + i + 4, remainder - 4))
+ *auth_mode = AUTH_MSSNTP;
+ else if (remainder == 72 && is_zero_data(data + i + 8, remainder - 8))
+ *auth_mode = AUTH_MSSNTP_EXT;
+ }
+ } else {
+ *auth_mode = AUTH_NONE;
+ *key_id = 0;
+ }
return 0;
}
@@ -1154,19 +1255,23 @@ check_packet_auth(NTP_Packet *pkt, int length, int *has_auth, uint32_t *key_id)
/* ================================================== */
static int
-receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance inst, NTP_Local_Address *local_addr, int length)
+receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
- int pkt_leap;
+ SST_Stats stats;
+
+ int pkt_leap, pkt_version;
uint32_t pkt_refid, pkt_key_id;
double pkt_root_delay;
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 timeval sample_time;
+ 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
@@ -1182,14 +1287,9 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* The skew and estimated frequency offset relative to the remote source */
double skew, source_freq_lo, source_freq_hi;
- /* These are the timeval equivalents of the remote epochs */
- struct timeval remote_receive_tv, remote_transmit_tv;
- struct timeval local_average, remote_average;
- double local_interval, remote_interval;
-
/* RFC 5905 packet tests */
- int test1, test2, test3, test5, test6, test7;
- int valid_packet;
+ int test1, test2n, test2i, test2, test3, test5, test6, test7;
+ int interleaved_packet, valid_packet, synced_packet;
/* Additional tests */
int testA, testB, testC, testD;
@@ -1198,9 +1298,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Kiss-o'-Death codes */
int kod_rate;
- /* Characters used to print synchronisation status */
- static const char sync_stats[4] = {'N', '+', '-', '?'};
-
/* 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. */
@@ -1210,36 +1307,41 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
measurement in seconds */
double error_in_estimate;
+ double remote_interval, local_interval, server_interval;
double delay_time, precision;
- int requeue_transmit;
+ NTP_Timestamp_Source sample_rx_tss;
/* ==================== */
+ stats = SRC_GetSourcestats(inst->source);
+
+ inst->report.total_rx_count++;
+
pkt_leap = NTP_LVM_TO_LEAP(message->lvm);
+ pkt_version = NTP_LVM_TO_VERSION(message->lvm);
pkt_refid = ntohl(message->reference_id);
- pkt_root_delay = UTI_Int32ToDouble(message->root_delay);
- pkt_root_dispersion = UTI_Int32ToDouble(message->root_dispersion);
-
- UTI_Int64ToTimeval(&message->receive_ts, &remote_receive_tv);
- UTI_Int64ToTimeval(&message->transmit_ts, &remote_transmit_tv);
+ pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
+ pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
/* Check if the packet is valid per RFC 5905, section 8.
The test values are 1 when passed and 0 when failed. */
/* Test 1 checks for duplicate packet */
- test1 = message->transmit_ts.hi != inst->remote_orig.hi ||
- message->transmit_ts.lo != inst->remote_orig.lo;
-
- /* Test 2 checks for bogus packet. This ensures the source is responding to
- the latest packet we sent to it. */
- test2 = message->originate_ts.hi == inst->local_ntp_tx.hi &&
- message->originate_ts.lo == inst->local_ntp_tx.lo;
+ test1 = !!UTI_CompareNtp64(&message->transmit_ts, &inst->remote_ntp_tx);
+
+ /* Test 2 checks for bogus packet in the basic and interleaved modes. This
+ ensures the source is responding to the latest packet we sent to it. */
+ test2n = !UTI_CompareNtp64(&message->originate_ts, &inst->local_ntp_tx);
+ test2i = inst->interleaved &&
+ !UTI_CompareNtp64(&message->originate_ts, &inst->local_ntp_rx);
+ test2 = test2n || test2i;
+ interleaved_packet = !test2n && test2i;
/* Test 3 checks for invalid timestamps. This can happen when the
association if not properly 'up'. */
- test3 = (message->originate_ts.hi || message->originate_ts.lo) &&
- (message->receive_ts.hi || message->receive_ts.lo) &&
- (message->transmit_ts.hi || message->transmit_ts.lo);
+ test3 = !UTI_IsZeroNtp64(&message->originate_ts) &&
+ !UTI_IsZeroNtp64(&message->receive_ts) &&
+ !UTI_IsZeroNtp64(&message->transmit_ts);
/* Test 4 would check for denied access. It would always pass as this
function is called only for known sources. */
@@ -1249,9 +1351,9 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
is bad, or it's authenticated with a different key than expected, it's got
to fail. If we don't expect the packet to be authenticated, just ignore
the test. */
- test5 = !inst->do_auth ||
- (check_packet_auth(message, length, NULL, &pkt_key_id) &&
- pkt_key_id == inst->auth_key_id);
+ test5 = inst->auth_mode == AUTH_NONE ||
+ (check_packet_auth(message, length, &pkt_auth_mode, &pkt_key_id) &&
+ pkt_auth_mode == inst->auth_mode && pkt_key_id == inst->auth_key_id);
/* Test 6 checks for unsynchronised server */
test6 = pkt_leap != LEAP_Unsynchronised &&
@@ -1262,8 +1364,10 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
defined maximum. */
test7 = pkt_root_delay / 2.0 + pkt_root_dispersion < NTP_MAX_DISPERSION;
- /* The packet is considered valid if the tests above passed */
- valid_packet = test1 && test2 && test3 && test5 && test6 && test7;
+ /* The packet is considered valid if the tests 1-5 passed. The timestamps
+ can be used for synchronisation if the tests 6 and 7 passed too. */
+ valid_packet = test1 && test2 && test3 && test5;
+ synced_packet = valid_packet && test6 && test7;
/* Check for Kiss-o'-Death codes */
kod_rate = 0;
@@ -1273,34 +1377,44 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
kod_rate = 1;
}
- /* The transmit timestamp and local receive timestamp must not be saved when
- the authentication test failed to prevent denial-of-service attacks on
- symmetric associations using authentication */
- if (test5) {
- inst->remote_orig = message->transmit_ts;
- inst->local_rx = *now;
- }
-
- /* This protects against replay of the last packet we sent */
- if (test2)
- inst->local_ntp_tx.hi = inst->local_ntp_tx.lo = 0;
+ if (synced_packet && (!interleaved_packet || inst->valid_timestamps)) {
+ /* These are the timespec equivalents of the remote and local epochs */
+ struct timespec remote_receive, remote_transmit, prev_remote_receive;
+ struct timespec local_average, remote_average;
+ double rx_ts_err;
- if (valid_packet) {
precision = LCL_GetSysPrecisionAsQuantum() +
UTI_Log2ToDouble(message->precision);
- SRC_GetFrequencyRange(inst->source, &source_freq_lo, &source_freq_hi);
+ SST_GetFrequencyRange(stats, &source_freq_lo, &source_freq_hi);
- UTI_AverageDiffTimevals(&remote_receive_tv, &remote_transmit_tv,
- &remote_average, &remote_interval);
-
- UTI_AverageDiffTimevals(&inst->local_tx, now,
- &local_average, &local_interval);
+ UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive);
+ UTI_Ntp64ToTimespec(&message->transmit_ts, &remote_transmit);
+
+ /* Calculate intervals between remote and local timestamps */
+ if (interleaved_packet) {
+ UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &prev_remote_receive);
+ UTI_AverageDiffTimespecs(&remote_transmit, &remote_receive,
+ &remote_average, &remote_interval);
+ UTI_AverageDiffTimespecs(&inst->local_rx.ts, &inst->local_tx.ts,
+ &local_average, &local_interval);
+ server_interval = UTI_DiffTimespecsToDouble(&remote_transmit,
+ &prev_remote_receive);
+ rx_ts_err = inst->local_rx.err;
+ sample_rx_tss = inst->local_rx.source;
+ } else {
+ UTI_AverageDiffTimespecs(&remote_receive, &remote_transmit,
+ &remote_average, &remote_interval);
+ UTI_AverageDiffTimespecs(&inst->local_tx.ts, &rx_ts->ts,
+ &local_average, &local_interval);
+ server_interval = remote_interval;
+ rx_ts_err = rx_ts->err;
+ sample_rx_tss = rx_ts->source;
+ }
- /* In our case, we work out 'delay' as the worst case delay,
- assuming worst case frequency error between us and the other
- source */
- delay = local_interval - remote_interval * (1.0 + source_freq_lo);
+ /* Work out 'delay' relative to the source's time */
+ delay = (1.0 - (source_freq_lo + source_freq_hi) / 2.0) *
+ local_interval - remote_interval;
/* Clamp delay to avoid misleading results later */
delay = fabs(delay);
@@ -1309,8 +1423,11 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Calculate offset. Following the NTP definition, this is negative
if we are fast of the remote source. */
- UTI_DiffTimevalsToDouble(&offset, &remote_average, &local_average);
-
+ offset = UTI_DiffTimespecsToDouble(&remote_average, &local_average);
+
+ /* Apply configured correction */
+ 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
@@ -1322,37 +1439,43 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
skew = (source_freq_hi - source_freq_lo) / 2.0;
/* and then calculate peer dispersion */
- dispersion = precision + now_err + skew * fabs(local_interval);
+ dispersion = precision + inst->local_tx.err + rx_ts_err + skew * fabs(local_interval);
/* Additional tests required to pass before accumulating the sample */
- /* Test A requires that the peer delay is not larger than the configured
- maximum and in client mode also that the server processing time is sane */
- testA = delay <= inst->max_delay &&
- (inst->mode != MODE_CLIENT || remote_interval <= MAX_SERVER_INTERVAL);
+ /* Test A requires that the minimum estimate of the peer delay is not
+ larger than the configured maximum, in client mode that the server
+ processing time is sane, and in the interleaved symmetric mode that
+ the delay is not longer than half of the remote polling interval to
+ detect missed packets */
+ testA = delay - dispersion <= inst->max_delay &&
+ !(inst->mode == MODE_CLIENT && server_interval > MAX_SERVER_INTERVAL) &&
+ !(inst->mode == MODE_ACTIVE && interleaved_packet &&
+ delay > UTI_Log2ToDouble(message->poll - 1));
/* Test B requires 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 = inst->max_delay_ratio <= 1.0 ||
- delay / SRC_MinRoundTripDelay(inst->source) <= inst->max_delay_ratio;
+ (delay - dispersion) / SST_MinRoundTripDelay(stats) <= inst->max_delay_ratio;
/* 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 = SRC_IsGoodSample(inst->source, -offset, delay,
- inst->max_delay_dev_ratio, LCL_GetMaxClockError(),
- &sample_time);
+ testC = SST_IsGoodSample(stats, -offset, delay, inst->max_delay_dev_ratio,
+ LCL_GetMaxClockError(), &sample_time);
/* Test D requires that the remote peer is not synchronised to us to
prevent a synchronisation loop */
testD = message->stratum <= 1 || REF_GetMode() != REF_ModeNormal ||
pkt_refid != UTI_IPToRefid(&local_addr->ip_addr);
} else {
+ remote_interval = local_interval = server_interval = 0.0;
offset = delay = dispersion = 0.0;
- sample_time = *now;
+ sample_time = rx_ts->ts;
+ sample_rx_tss = rx_ts->source;
testA = testB = testC = testD = 0;
}
@@ -1364,44 +1487,74 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
root_dispersion = pkt_root_dispersion + dispersion;
distance = dispersion + 0.5 * delay;
+ /* 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
+ interleaved mode. Protect the timestamps against replay attacks in client
+ mode, and also in symmetric mode as long as the peers use the same polling
+ interval and never start with clocks in future or very distant past.
+ The authentication test (test5) is required to prevent DoS attacks using
+ unauthenticated packets on authenticated symmetric associations. */
+ if ((inst->mode == MODE_CLIENT && valid_packet && !inst->valid_rx) ||
+ (inst->mode == MODE_ACTIVE && (valid_packet || !inst->valid_rx) &&
+ test5 && !UTI_IsZeroNtp64(&message->transmit_ts) &&
+ (!inst->updated_timestamps || (valid_packet && !inst->valid_rx) ||
+ UTI_CompareNtp64(&inst->remote_ntp_tx, &message->transmit_ts) < 0))) {
+ inst->remote_ntp_rx = message->receive_ts;
+ inst->remote_ntp_tx = message->transmit_ts;
+ inst->local_rx = *rx_ts;
+ inst->valid_timestamps = synced_packet;
+ inst->updated_timestamps = 1;
+ }
+
+ /* Accept at most one response per request. The NTP specification recommends
+ resetting local_ntp_tx to make the following packets fail test2 or test3,
+ but that would not allow the code above to make multiple updates of the
+ timestamps in symmetric mode. Also, ignore presend responses. */
+ if (inst->valid_rx) {
+ test2 = test3 = 0;
+ valid_packet = synced_packet = good_packet = 0;
+ } else if (valid_packet) {
+ if (inst->presend_done) {
+ testA = 0;
+ good_packet = 0;
+ }
+ inst->valid_rx = 1;
+ }
+
+ if ((int)sample_rx_tss < 0 || sample_rx_tss >= sizeof (tss_chars) ||
+ (int)inst->local_tx.source < 0 || inst->local_tx.source >= sizeof (tss_chars))
+ assert(0);
+
DEBUG_LOG(LOGF_NtpCore, "NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%f root_disp=%f refid=%"PRIx32" [%s]",
message->lvm, message->stratum, message->poll, message->precision,
pkt_root_delay, pkt_root_dispersion, pkt_refid,
message->stratum == NTP_INVALID_STRATUM ? UTI_RefidToString(pkt_refid) : "");
DEBUG_LOG(LOGF_NtpCore, "reference=%s origin=%s receive=%s transmit=%s",
- UTI_TimestampToString(&message->reference_ts),
- UTI_TimestampToString(&message->originate_ts),
- UTI_TimestampToString(&message->receive_ts),
- UTI_TimestampToString(&message->transmit_ts));
- DEBUG_LOG(LOGF_NtpCore, "offset=%f delay=%f dispersion=%f root_delay=%f root_dispersion=%f",
+ UTI_Ntp64ToString(&message->reference_ts),
+ UTI_Ntp64ToString(&message->originate_ts),
+ UTI_Ntp64ToString(&message->receive_ts),
+ UTI_Ntp64ToString(&message->transmit_ts));
+ DEBUG_LOG(LOGF_NtpCore, "offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f",
offset, delay, dispersion, root_delay, root_dispersion);
- DEBUG_LOG(LOGF_NtpCore, "test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d valid=%d good=%d",
+ DEBUG_LOG(LOGF_NtpCore, "remote_interval=%.9f local_interval=%.9f server_interval=%.9f txs=%c rxs=%c",
+ remote_interval, local_interval, server_interval,
+ tss_chars[inst->local_tx.source], tss_chars[sample_rx_tss]);
+ DEBUG_LOG(LOGF_NtpCore, "test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d presend=%d valid=%d good=%d updated=%d",
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
- kod_rate, valid_packet, good_packet);
-
- requeue_transmit = 0;
-
- /* Reduce polling rate if KoD RATE was received */
- if (kod_rate) {
- /* Stop ongoing burst */
- if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) {
- inst->burst_good_samples_to_go = 0;
- LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s, burst sampling stopped",
- UTI_IPToString(&inst->remote_addr.ip_addr));
- }
-
- requeue_transmit = 1;
- }
+ kod_rate, interleaved_packet, inst->presend_done, valid_packet, good_packet,
+ !UTI_CompareTimespecs(&inst->local_rx.ts, &rx_ts->ts));
if (valid_packet) {
- inst->remote_poll = message->poll;
- inst->remote_stratum = message->stratum;
- inst->tx_count = 0;
- SRC_UpdateReachability(inst->source, 1);
+ if (synced_packet) {
+ inst->remote_poll = message->poll;
+ inst->remote_stratum = message->stratum;
+ inst->tx_count = 0;
+ SRC_UpdateReachability(inst->source, 1);
+ }
if (good_packet) {
/* Do this before we accumulate a new sample into the stats registers, obviously */
- estimated_offset = SRC_PredictOffset(inst->source, &sample_time);
+ estimated_offset = SST_PredictOffset(stats, &sample_time);
SRC_AccumulateSample(inst->source,
&sample_time,
@@ -1437,7 +1590,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
default:
break;
}
- } else {
+ } else if (synced_packet) {
/* Slowly increase the polling interval if we can't get good packet */
adjust_poll(inst, 0.1);
}
@@ -1449,38 +1602,75 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Update the local address */
inst->local_addr.ip_addr = local_addr->ip_addr;
- requeue_transmit = 1;
- }
+ /* And now, requeue the timer */
+ if (inst->opmode != MD_OFFLINE) {
+ delay_time = get_transmit_delay(inst, 0,
+ UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts));
- /* And now, requeue the timer. */
- if (requeue_transmit && inst->opmode != MD_OFFLINE) {
- delay_time = get_transmit_delay(inst, 0, local_interval);
+ if (kod_rate) {
+ /* Back off for a while and stop ongoing burst */
+ delay_time += 4 * (1UL << inst->minpoll);
- if (kod_rate) {
- /* Back off for a while */
- delay_time += (double) (4 * (1UL << inst->minpoll));
+ if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) {
+ inst->burst_good_samples_to_go = 0;
+ LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s, burst sampling stopped",
+ UTI_IPToString(&inst->remote_addr.ip_addr));
+ }
+ }
+
+ /* Get rid of old timeout and start a new one */
+ assert(inst->tx_timeout_id);
+ restart_timeout(inst, delay_time);
}
- /* Get rid of old timeout and start a new one */
- assert(inst->tx_timeout_id);
- restart_timeout(inst, delay_time);
+ /* Update the NTP report */
+ inst->report.remote_addr = inst->remote_addr.ip_addr;
+ inst->report.local_addr = inst->local_addr.ip_addr;
+ inst->report.remote_port = inst->remote_addr.port;
+ inst->report.leap = pkt_leap;
+ inst->report.version = pkt_version;
+ inst->report.mode = NTP_LVM_TO_MODE(message->lvm);
+ inst->report.stratum = message->stratum;
+ inst->report.poll = message->poll;
+ inst->report.precision = message->precision;
+ inst->report.root_delay = pkt_root_delay;
+ 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.response_time = server_interval;
+ inst->report.jitter_asymmetry = SST_GetJitterAsymmetry(stats);
+ inst->report.tests = ((((((((test1 << 1 | test2) << 1 | test3) << 1 |
+ test5) << 1 | test6) << 1 | test7) << 1 |
+ testA) << 1 | testB) << 1 | testC) << 1 | testD;
+ inst->report.interleaved = interleaved_packet;
+ inst->report.authenticated = inst->auth_mode != AUTH_NONE;
+ inst->report.tx_tss_char = tss_chars[inst->local_tx.source];
+ inst->report.rx_tss_char = tss_chars[sample_rx_tss];
+
+ inst->report.total_valid_count++;
}
/* Do measurement logging */
if (logfileid != -1) {
- 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",
+ 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_IPToString(&inst->remote_addr.ip_addr),
- sync_stats[pkt_leap],
+ leap_chars[pkt_leap],
message->stratum,
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
- inst->local_poll, inst->remote_poll,
+ inst->local_poll, message->poll,
inst->poll_score,
offset, delay, dispersion,
- pkt_root_delay, pkt_root_dispersion);
+ pkt_root_delay, pkt_root_dispersion, pkt_refid,
+ NTP_LVM_TO_MODE(message->lvm), interleaved_packet ? 'I' : 'B',
+ tss_chars[inst->local_tx.source],
+ tss_chars[sample_rx_tss]);
}
- return valid_packet;
+ return good_packet;
}
/* ================================================== */
@@ -1501,8 +1691,8 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
| Bcast Client 6 | DSCRD | DSCRD | DSCRD | DSCRD | PROC |
+------------------+-------+-------+-------+-------+-------+
- Association mode 0 is implemented in NCR_ProcessUnknown(), other modes
- in NCR_ProcessKnown().
+ Association mode 0 is implemented in NCR_ProcessRxUnknown(), other modes
+ in NCR_ProcessRxKnown().
Broadcast, manycast and ephemeral symmetric passive associations are not
supported yet.
@@ -1513,14 +1703,8 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
and it relates to a source we have an ongoing protocol exchange with */
int
-NCR_ProcessKnown
-(NTP_Packet *message, /* the received message */
- struct timeval *now, /* timestamp at time of receipt */
- double now_err,
- NCR_Instance inst, /* the instance record for this peer/server */
- NTP_Local_Address *local_addr, /* the receiving address */
- int length /* the length of the received packet */
- )
+NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
int pkt_mode, proc_packet, proc_as_unknown;
@@ -1588,10 +1772,6 @@ NCR_ProcessKnown
break;
case MODE_SERVER:
- /* Ignore presend reply */
- if (inst->presend_done)
- break;
-
switch (inst->mode) {
case MODE_CLIENT:
/* Standard case where he's a server and we're the client */
@@ -1627,10 +1807,9 @@ NCR_ProcessKnown
return 0;
}
- return receive_packet(message, now, now_err, inst, local_addr, length);
+ return receive_packet(inst, local_addr, rx_ts, message, length);
} else if (proc_as_unknown) {
- NCR_ProcessUnknown(message, now, now_err, &inst->remote_addr,
- local_addr, length);
+ NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length);
/* It's not a reply to our request, don't return success */
return 0;
} else {
@@ -1645,17 +1824,14 @@ NCR_ProcessKnown
and it relates to a source we don't know (not our server or peer) */
void
-NCR_ProcessUnknown
-(NTP_Packet *message, /* the received message */
- struct timeval *now, /* timestamp at time of receipt */
- double now_err, /* assumed error in the timestamp */
- NTP_Remote_Address *remote_addr,
- NTP_Local_Address *local_addr,
- int length /* the length of the received packet */
- )
+NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
NTP_Mode pkt_mode, my_mode;
- int has_auth, valid_auth, log_index;
+ NTP_int64 *local_ntp_rx, *local_ntp_tx;
+ NTP_Local_Timestamp local_tx, *tx_ts;
+ int valid_auth, log_index, interleaved;
+ AuthenticationMode auth_mode;
uint32_t key_id;
/* Ignore the packet if it wasn't received by server socket */
@@ -1692,7 +1868,7 @@ NCR_ProcessUnknown
return;
}
- log_index = CLG_LogNTPAccess(&remote_addr->ip_addr, now);
+ log_index = CLG_LogNTPAccess(&remote_addr->ip_addr, &rx_ts->ts);
/* Don't reply to all requests if the rate is excessive */
if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) {
@@ -1701,35 +1877,156 @@ NCR_ProcessUnknown
}
/* Check if the packet includes MAC that authenticates properly */
- valid_auth = check_packet_auth(message, length, &has_auth, &key_id);
-
- /* If authentication failed, reply with crypto-NAK */
- if (!valid_auth)
- key_id = 0;
-
- /* Send a reply.
- - copy the poll value as the client may use it to control its polling
- interval
- - authenticate the packet if the request was authenticated
- - originate timestamp is the client's transmit time
- - don't save our transmit timestamp as we aren't maintaining state about
- this client */
- transmit_packet(my_mode, message->poll, NTP_LVM_TO_VERSION(message->lvm),
- has_auth, key_id, &message->transmit_ts, now, NULL, NULL,
- remote_addr, local_addr);
+ valid_auth = check_packet_auth(message, length, &auth_mode, &key_id);
+
+ /* If authentication failed, select whether and how we should respond */
+ if (!valid_auth) {
+ switch (auth_mode) {
+ case AUTH_NONE:
+ /* Reply with no MAC */
+ break;
+ case AUTH_MSSNTP:
+ /* Ignore the failure (MS-SNTP servers don't check client MAC) */
+ break;
+ default:
+ /* Discard packets in other modes */
+ DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded auth_mode=%d", auth_mode);
+ return;
+ }
+ }
+
+ local_ntp_rx = local_ntp_tx = NULL;
+ tx_ts = NULL;
+ interleaved = 0;
+
+ /* Check if the client is using the interleaved mode. If it is, save the
+ new transmit timestamp and if the old transmit timestamp is valid, respond
+ in the interleaved mode. This means the third reply to a new client is
+ the earliest one that can be interleaved. We don't want to waste time
+ on clients that are not using the interleaved mode. */
+ if (log_index >= 0) {
+ CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
+ interleaved = !UTI_IsZeroNtp64(local_ntp_rx) &&
+ !UTI_CompareNtp64(&message->originate_ts, local_ntp_rx);
+
+ if (interleaved) {
+ if (!UTI_IsZeroNtp64(local_ntp_tx))
+ UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
+ else
+ interleaved = 0;
+ tx_ts = &local_tx;
+ } else {
+ UTI_ZeroNtp64(local_ntp_tx);
+ local_ntp_tx = NULL;
+ }
+ }
+
+ /* Send a reply */
+ transmit_packet(my_mode, interleaved, message->poll, NTP_LVM_TO_VERSION(message->lvm),
+ auth_mode, key_id, &message->receive_ts, &message->transmit_ts,
+ rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr);
+
+ /* Save the transmit timestamp */
+ if (tx_ts)
+ UTI_TimespecToNtp64(&tx_ts->ts, local_ntp_tx, NULL);
+}
+
+/* ================================================== */
+
+static void
+update_tx_timestamp(NTP_Local_Timestamp *tx_ts, NTP_Local_Timestamp *new_tx_ts,
+ NTP_int64 *local_ntp_rx, NTP_int64 *local_ntp_tx, NTP_Packet *message)
+{
+ double delay;
+
+ if (UTI_IsZeroTimespec(&tx_ts->ts)) {
+ DEBUG_LOG(LOGF_NtpCore, "Unexpected TX update");
+ return;
+ }
+
+ /* Check if this is the last packet that was sent */
+ if ((local_ntp_rx && UTI_CompareNtp64(&message->receive_ts, local_ntp_rx)) ||
+ (local_ntp_tx && UTI_CompareNtp64(&message->transmit_ts, local_ntp_tx))) {
+ DEBUG_LOG(LOGF_NtpCore, "RX/TX timestamp mismatch");
+ return;
+ }
+
+ delay = UTI_DiffTimespecsToDouble(&new_tx_ts->ts, &tx_ts->ts);
+
+ if (delay < 0.0 || delay > MAX_TX_DELAY) {
+ DEBUG_LOG(LOGF_NtpCore, "Unacceptable TX delay %.9f", delay);
+ return;
+ }
+
+ *tx_ts = *new_tx_ts;
+
+ DEBUG_LOG(LOGF_NtpCore, "Updated TX timestamp delay=%.9f", delay);
+}
+
+/* ================================================== */
+
+void
+NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
+{
+ NTP_Mode pkt_mode;
+
+ if (!check_packet_format(message, length))
+ return;
+
+ pkt_mode = NTP_LVM_TO_MODE(message->lvm);
+
+ /* Server and passive mode packets are responses to unknown sources */
+ if (pkt_mode != MODE_CLIENT && pkt_mode != MODE_ACTIVE) {
+ NCR_ProcessTxUnknown(&inst->remote_addr, local_addr, tx_ts, message, length);
+ return;
+ }
+
+ update_tx_timestamp(&inst->local_tx, tx_ts, &inst->local_ntp_rx, &inst->local_ntp_tx,
+ message);
}
/* ================================================== */
void
-NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset)
+NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
+{
+ NTP_int64 *local_ntp_rx, *local_ntp_tx;
+ NTP_Local_Timestamp local_tx;
+ int log_index;
+
+ if (!check_packet_format(message, length))
+ return;
+
+ if (NTP_LVM_TO_MODE(message->lvm) == MODE_BROADCAST)
+ return;
+
+ log_index = CLG_GetClientIndex(&remote_addr->ip_addr);
+ if (log_index < 0)
+ return;
+
+ CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
+
+ if (UTI_IsZeroNtp64(local_ntp_tx))
+ return;
+
+ UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
+ update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
+ UTI_TimespecToNtp64(&local_tx.ts, local_ntp_tx, NULL);
+}
+
+/* ================================================== */
+
+void
+NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset)
{
double delta;
- if (inst->local_rx.tv_sec || inst->local_rx.tv_usec)
- UTI_AdjustTimeval(&inst->local_rx, when, &inst->local_rx, &delta, dfreq, doffset);
- if (inst->local_tx.tv_sec || inst->local_tx.tv_usec)
- UTI_AdjustTimeval(&inst->local_tx, when, &inst->local_tx, &delta, dfreq, doffset);
+ if (!UTI_IsZeroTimespec(&inst->local_rx.ts))
+ UTI_AdjustTimespec(&inst->local_rx.ts, when, &inst->local_rx.ts, &delta, dfreq, doffset);
+ if (!UTI_IsZeroTimespec(&inst->local_tx.ts))
+ UTI_AdjustTimespec(&inst->local_tx.ts, when, &inst->local_tx.ts, &delta, dfreq, doffset);
}
/* ================================================== */
@@ -1894,7 +2191,7 @@ NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_sampl
/* ================================================== */
void
-NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now)
+NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now)
{
report->poll = inst->local_poll;
@@ -1912,6 +2209,14 @@ NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *no
/* ================================================== */
+void
+NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report)
+{
+ *report = inst->report;
+}
+
+/* ================================================== */
+
int
NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
{
@@ -2027,17 +2332,17 @@ broadcast_timeout(void *arg)
{
BroadcastDestination *destination;
NTP_int64 orig_ts;
- struct timeval recv_ts;
+ NTP_Local_Timestamp recv_ts;
destination = ARR_GetElement(broadcasts, (long)arg);
- orig_ts.hi = 0;
- orig_ts.lo = 0;
- recv_ts.tv_sec = 0;
- recv_ts.tv_usec = 0;
+ UTI_ZeroNtp64(&orig_ts);
+ UTI_ZeroTimespec(&recv_ts.ts);
+ recv_ts.source = NTP_TS_DAEMON;
+ recv_ts.err = 0.0;
- transmit_packet(MODE_BROADCAST, 6 /* FIXME: should this be log2(interval)? */,
- NTP_VERSION, 0, 0, &orig_ts, &recv_ts, NULL, NULL,
+ transmit_packet(MODE_BROADCAST, 0, log(destination->interval) / log(2.0) + 0.5,
+ NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts, NULL, NULL, NULL,
&destination->addr, &destination->local_addr);
/* Requeue timeout. We don't care if interval drifts gradually. */
@@ -2058,7 +2363,7 @@ NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval)
destination->addr.port = port;
destination->local_addr.ip_addr.family = IPADDR_UNSPEC;
destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr);
- destination->interval = interval;
+ destination->interval = CLAMP(1 << MIN_POLL, interval, 1 << MAX_POLL);
SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS,
SCH_NtpBroadcastClass, broadcast_timeout,
diff --git a/ntp_core.h b/ntp_core.h
index d0af70f..f788d68 100644
--- a/ntp_core.h
+++ b/ntp_core.h
@@ -38,6 +38,18 @@ typedef enum {
NTP_SERVER, NTP_PEER
} NTP_Source_Type;
+typedef enum {
+ NTP_TS_DAEMON = 0,
+ NTP_TS_KERNEL,
+ NTP_TS_HARDWARE
+} NTP_Timestamp_Source;
+
+typedef struct {
+ struct timespec ts;
+ double err;
+ NTP_Timestamp_Source source;
+} NTP_Local_Timestamp;
+
/* This is a private data type used for storing the instance record for
each source that we are chiming with */
typedef struct NCR_Instance_Record *NCR_Instance;
@@ -58,19 +70,34 @@ extern void NCR_StartInstance(NCR_Instance instance);
/* Reset an instance */
extern void NCR_ResetInstance(NCR_Instance inst);
+/* Reset polling interval of an instance */
+extern void NCR_ResetPoll(NCR_Instance instance);
+
/* Change the remote address of an instance */
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
/* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */
-extern int NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, NTP_Local_Address *local_addr, int length);
+extern int NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a new packet arrives off the network,
and we do not recognize its source */
-extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
+extern void NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
+
+/* This routine is called when a packet is sent to a source we have
+ an ongoing protocol exchange with */
+extern void NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
+
+/* This routine is called when a packet is sent to a destination we
+ do not recognize */
+extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Slew receive and transmit times in instance records */
-extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);
+extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
/* Take a particular source online (i.e. start sampling it) */
extern void NCR_TakeSourceOnline(NCR_Instance inst);
@@ -95,7 +122,8 @@ extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
-extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now);
+extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
+extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
extern int NCR_CheckAccessRestriction(IPAddr *ip_addr);
diff --git a/ntp_io.c b/ntp_io.c
index 88fc76e..380e2f1 100644
--- a/ntp_io.c
+++ b/ntp_io.c
@@ -30,6 +30,7 @@
#include "sysincl.h"
+#include "array.h"
#include "ntp_io.h"
#include "ntp_core.h"
#include "ntp_sources.h"
@@ -40,7 +41,12 @@
#include "privops.h"
#include "util.h"
+#ifdef HAVE_LINUX_TIMESTAMPING
+#include "ntp_io_linux.h"
+#endif
+
#define INVALID_SOCK_FD -1
+#define CMSGBUF_SIZE 256
union sockaddr_in46 {
struct sockaddr_in in4;
@@ -50,6 +56,31 @@ union sockaddr_in46 {
struct sockaddr u;
};
+struct Message {
+ union sockaddr_in46 name;
+ struct iovec iov;
+ NTP_Receive_Buffer buf;
+ /* Aligned buffer for control messages */
+ struct cmsghdr cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
+};
+
+#ifdef HAVE_RECVMMSG
+#define MAX_RECV_MESSAGES 4
+#define MessageHeader mmsghdr
+#else
+/* Compatible with mmsghdr */
+struct MessageHeader {
+ struct msghdr msg_hdr;
+ unsigned int msg_len;
+};
+
+#define MAX_RECV_MESSAGES 1
+#endif
+
+/* Arrays of Message and MessageHeader */
+static ARR_Instance recv_messages;
+static ARR_Instance recv_headers;
+
/* The server/peer and client sockets for IPv4 and IPv6 */
static int server_sock_fd4;
static int client_sock_fd4;
@@ -80,7 +111,7 @@ static int initialised=0;
/* ================================================== */
/* Forward prototypes */
-static void read_from_socket(void *anything);
+static void read_from_socket(int sock_fd, int event, void *anything);
/* ================================================== */
@@ -91,8 +122,8 @@ prepare_socket(int family, int port_number, int client_only)
socklen_t my_addr_len;
int sock_fd;
IPAddr bind_address;
- int on_off = 1;
-
+ int events = SCH_FILE_INPUT, on_off = 1;
+
/* Open Internet domain UDP socket for NTP message transmissions */
sock_fd = socket(family, SOCK_DGRAM, 0);
@@ -175,12 +206,20 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef SO_TIMESTAMP
/* Enable receiving of timestamp control messages */
+#ifdef SO_TIMESTAMPNS
+ /* Try nanosecond resolution first */
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
+#endif
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP");
/* Don't quit - we might survive anyway */
}
#endif
+#ifdef HAVE_LINUX_TIMESTAMPING
+ NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events);
+#endif
+
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
@@ -229,8 +268,8 @@ prepare_socket(int family, int port_number, int client_only)
return INVALID_SOCK_FD;
}
- /* Register handler for read events on the socket */
- SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
+ /* Register handler for read and possibly exception events on the socket */
+ SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
return sock_fd;
}
@@ -282,11 +321,38 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
- SCH_RemoveInputFileHandler(sock_fd);
+ SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
}
/* ================================================== */
+
+static void
+prepare_buffers(unsigned int n)
+{
+ struct MessageHeader *hdr;
+ struct Message *msg;
+ unsigned int i;
+
+ for (i = 0; i < n; i++) {
+ msg = ARR_GetElement(recv_messages, i);
+ hdr = ARR_GetElement(recv_headers, i);
+
+ msg->iov.iov_base = &msg->buf;
+ msg->iov.iov_len = sizeof (msg->buf);
+ hdr->msg_hdr.msg_name = &msg->name;
+ hdr->msg_hdr.msg_namelen = sizeof (msg->name);
+ hdr->msg_hdr.msg_iov = &msg->iov;
+ hdr->msg_hdr.msg_iovlen = 1;
+ hdr->msg_hdr.msg_control = &msg->cmsgbuf;
+ hdr->msg_hdr.msg_controllen = sizeof (msg->cmsgbuf);
+ hdr->msg_hdr.msg_flags = 0;
+ hdr->msg_len = 0;
+ }
+}
+
+/* ================================================== */
+
void
NIO_Initialise(int family)
{
@@ -295,6 +361,19 @@ NIO_Initialise(int family)
assert(!initialised);
initialised = 1;
+#ifdef HAVE_LINUX_TIMESTAMPING
+ NIO_Linux_Initialise();
+#else
+ if (ARR_GetSize(CNF_GetHwTsInterfaces()))
+ LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
+#endif
+
+ recv_messages = ARR_CreateInstance(sizeof (struct Message));
+ ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
+ recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
+ ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
+ prepare_buffers(MAX_RECV_MESSAGES);
+
server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort();
@@ -367,6 +446,13 @@ NIO_Finalise(void)
close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#endif
+ ARR_DestroyInstance(recv_headers);
+ ARR_DestroyInstance(recv_messages);
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+ NIO_Linux_Finalise();
+#endif
+
initialised = 0;
}
@@ -482,116 +568,174 @@ NIO_IsServerSocket(int sock_fd)
/* ================================================== */
static void
-read_from_socket(void *anything)
+process_message(struct msghdr *hdr, int length, int sock_fd)
{
- /* This should only be called when there is something
- to read, otherwise it will block. */
-
- int status, sock_fd;
- NTP_Receive_Buffer message;
- union sockaddr_in46 where_from;
- unsigned int flags = 0;
- struct timeval now;
- double now_err;
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
- char cmsgbuf[256];
- struct msghdr msg;
- struct iovec iov;
+ NTP_Local_Timestamp local_ts;
+ struct timespec sched_ts;
struct cmsghdr *cmsg;
+ int if_index;
- assert(initialised);
-
- SCH_GetLastEventTime(&now, &now_err, NULL);
+ SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
+ local_ts.source = NTP_TS_DAEMON;
+ sched_ts = local_ts.ts;
- iov.iov_base = &message.ntp_pkt;
- iov.iov_len = sizeof(message);
- msg.msg_name = &where_from;
- msg.msg_namelen = sizeof(where_from);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = (void *) cmsgbuf;
- msg.msg_controllen = sizeof(cmsgbuf);
- msg.msg_flags = 0;
-
- sock_fd = (long)anything;
- status = recvmsg(sock_fd, &msg, flags);
+ if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
+ DEBUG_LOG(LOGF_NtpIO, "Truncated source address");
+ return;
+ }
- /* Don't bother checking if read failed or why if it did. More
- likely than not, it will be connection refused, resulting from a
- previous sendto() directing a datagram at a port that is not
- listening (which appears to generate an ICMP response, and on
- some architectures e.g. Linux this is translated into an error
- reponse on a subsequent recvfrom). */
+ if (hdr->msg_namelen >= sizeof (((struct sockaddr *)hdr->msg_name)->sa_family)) {
+ UTI_SockaddrToIPAndPort((struct sockaddr *)hdr->msg_name,
+ &remote_addr.ip_addr, &remote_addr.port);
+ } else {
+ remote_addr.ip_addr.family = IPADDR_UNSPEC;
+ remote_addr.port = 0;
+ }
- if (status > 0) {
- if (msg.msg_namelen > sizeof (where_from))
- LOG_FATAL(LOGF_NtpIO, "Truncated source address");
+ local_addr.ip_addr.family = IPADDR_UNSPEC;
+ local_addr.sock_fd = sock_fd;
+ if_index = -1;
- UTI_SockaddrToIPAndPort(&where_from.u, &remote_addr.ip_addr, &remote_addr.port);
+ if (hdr->msg_flags & MSG_TRUNC) {
+ DEBUG_LOG(LOGF_NtpIO, "Received truncated message from %s:%d",
+ UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
+ return;
+ }
- local_addr.ip_addr.family = IPADDR_UNSPEC;
- local_addr.sock_fd = sock_fd;
+ if (hdr->msg_flags & MSG_CTRUNC) {
+ DEBUG_LOG(LOGF_NtpIO, "Truncated control message");
+ /* Continue */
+ }
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_IN_PKTINFO
- if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
- struct in_pktinfo ipi;
+ if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo ipi;
- memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
- local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
- local_addr.ip_addr.family = IPADDR_INET4;
- }
+ memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
+ local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
+ local_addr.ip_addr.family = IPADDR_INET4;
+ if_index = ipi.ipi_ifindex;
+ }
#endif
#ifdef HAVE_IN6_PKTINFO
- if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
- struct in6_pktinfo ipi;
-
- memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
- memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
- sizeof (local_addr.ip_addr.addr.in6));
- local_addr.ip_addr.family = IPADDR_INET6;
- }
+ if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo ipi;
+
+ memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
+ memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
+ sizeof (local_addr.ip_addr.addr.in6));
+ local_addr.ip_addr.family = IPADDR_INET6;
+ if_index = ipi.ipi6_ifindex;
+ }
#endif
-#ifdef SO_TIMESTAMP
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
- struct timeval tv;
+#ifdef SCM_TIMESTAMP
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
+ struct timeval tv;
+ struct timespec ts;
- memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
- LCL_CookTime(&tv, &now, &now_err);
- }
+ memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
+ UTI_TimevalToTimespec(&tv, &ts);
+ LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
+ local_ts.source = NTP_TS_KERNEL;
+ }
#endif
+
+#ifdef SCM_TIMESTAMPNS
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
+ struct timespec ts;
+
+ memcpy(&ts, CMSG_DATA(cmsg), sizeof (ts));
+ LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
+ local_ts.source = NTP_TS_KERNEL;
}
+#endif
+ }
+
+#ifdef HAVE_LINUX_TIMESTAMPING
+ if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts,
+ hdr, length, sock_fd, if_index))
+ return;
+#endif
+
+ DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
+ length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
+ UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, if_index,
+ local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
- DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
- status, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
- UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
+ /* Just ignore the packet if it's not of a recognized length */
+ if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer))
+ return;
- if (status >= NTP_NORMAL_PACKET_LENGTH) {
+ NSR_ProcessRx(&remote_addr, &local_addr, &local_ts,
+ (NTP_Packet *)hdr->msg_iov[0].iov_base, length);
+}
- NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
- &remote_addr, &local_addr, status);
+/* ================================================== */
- } else {
+static void
+read_from_socket(int sock_fd, int event, void *anything)
+{
+ /* This should only be called when there is something
+ to read, otherwise it may block */
- /* Just ignore the packet if it's not of a recognized length */
+ struct MessageHeader *hdr;
+ unsigned int i, n;
+ int status, flags = 0;
- }
+ hdr = ARR_GetElements(recv_headers);
+ n = ARR_GetSize(recv_headers);
+ assert(n >= 1);
+
+ if (event == SCH_FILE_EXCEPTION) {
+#ifdef HAVE_LINUX_TIMESTAMPING
+ flags |= MSG_ERRQUEUE;
+#else
+ assert(0);
+#endif
+ }
+
+#ifdef HAVE_RECVMMSG
+ status = recvmmsg(sock_fd, hdr, n, flags | MSG_DONTWAIT, NULL);
+ if (status >= 0)
+ n = status;
+#else
+ n = 1;
+ status = recvmsg(sock_fd, &hdr[0].msg_hdr, flags);
+ if (status >= 0)
+ hdr[0].msg_len = status;
+#endif
+
+ if (status < 0) {
+ DEBUG_LOG(LOGF_NtpIO, "Could not receive from fd %d : %s", sock_fd,
+ strerror(errno));
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ hdr = ARR_GetElement(recv_headers, i);
+ process_message(&hdr->msg_hdr, hdr->msg_len, sock_fd);
}
+
+ /* Restore the buffers to their original state */
+ prepare_buffers(n);
}
/* ================================================== */
/* Send a packet to remote address from local address */
-static int
-send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
+int
+NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
+ NTP_Local_Address *local_addr, int length, int process_tx)
{
union sockaddr_in46 remote;
struct msghdr msg;
struct iovec iov;
- char cmsgbuf[256];
+ struct cmsghdr *cmsg, cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
int cmsglen;
socklen_t addrlen = 0;
@@ -604,11 +748,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
}
/* Don't set address with connected socket */
- if (local_addr->sock_fd == server_sock_fd4 ||
-#ifdef FEAT_IPV6
- local_addr->sock_fd == server_sock_fd6 ||
-#endif
- !separate_client_sockets) {
+ if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port,
&remote.u);
if (!addrlen)
@@ -624,7 +764,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
}
iov.iov_base = packet;
- iov.iov_len = packetlen;
+ iov.iov_len = length;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
@@ -634,7 +774,6 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
#ifdef HAVE_IN_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) {
- struct cmsghdr *cmsg;
struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
@@ -652,7 +791,6 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
#ifdef HAVE_IN6_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET6) {
- struct cmsghdr *cmsg;
struct in6_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
@@ -669,6 +807,11 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
}
#endif
+#ifdef HAVE_LINUX_TIMESTAMPING
+ if (process_tx)
+ cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd);
+#endif
+
msg.msg_controllen = cmsglen;
/* This is apparently required on some systems */
if (!cmsglen)
@@ -682,18 +825,9 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
return 0;
}
- DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", packetlen,
+ DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", length,
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
return 1;
}
-
-/* ================================================== */
-/* Send a packet to a given address */
-
-int
-NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
-{
- return send_packet((void *) packet, length, remote_addr, local_addr);
-}
diff --git a/ntp_io.h b/ntp_io.h
index cbdee56..1bdcf12 100644
--- a/ntp_io.h
+++ b/ntp_io.h
@@ -54,6 +54,7 @@ extern void NIO_CloseServerSocket(int sock_fd);
extern int NIO_IsServerSocket(int sock_fd);
/* Function to transmit a packet */
-extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
+extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
+ NTP_Local_Address *local_addr, int length, int process_tx);
#endif /* GOT_NTP_IO_H */
diff --git a/ntp_io_linux.c b/ntp_io_linux.c
new file mode 100644
index 0000000..9cf791b
--- /dev/null
+++ b/ntp_io_linux.c
@@ -0,0 +1,578 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Functions for NTP I/O specific to Linux
+ */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include <linux/errqueue.h>
+#include <linux/ethtool.h>
+#include <linux/net_tstamp.h>
+#include <linux/ptp_clock.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+
+#include "array.h"
+#include "conf.h"
+#include "hwclock.h"
+#include "local.h"
+#include "logging.h"
+#include "ntp_core.h"
+#include "ntp_io.h"
+#include "ntp_io_linux.h"
+#include "ntp_sources.h"
+#include "sched.h"
+#include "sys_linux.h"
+#include "util.h"
+
+union sockaddr_in46 {
+ struct sockaddr_in in4;
+#ifdef FEAT_IPV6
+ struct sockaddr_in6 in6;
+#endif
+ struct sockaddr u;
+};
+
+struct Interface {
+ char name[IF_NAMESIZE];
+ int if_index;
+ int phc_fd;
+ /* Link speed in mbit/s */
+ int link_speed;
+ /* Start of UDP data at layer 2 for IPv4 and IPv6 */
+ int l2_udp4_ntp_start;
+ int l2_udp6_ntp_start;
+ HCL_Instance clock;
+};
+
+/* Number of PHC readings per HW clock sample */
+#define PHC_READINGS 10
+
+/* Array of Interfaces */
+static ARR_Instance interfaces;
+
+/* RX/TX and TX-specific timestamping socket options */
+static int ts_flags;
+static int ts_tx_flags;
+
+/* Flag indicating the socket options can't be changed in control messages */
+static int permanent_ts_options;
+
+/* ================================================== */
+
+static int
+add_interface(const char *name)
+{
+ struct ethtool_ts_info ts_info;
+ struct hwtstamp_config ts_config;
+ struct ifreq req;
+ int sock_fd, if_index, phc_index, phc_fd;
+ struct Interface *iface;
+ char phc_path[64];
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ return 0;
+
+ memset(&req, 0, sizeof (req));
+ memset(&ts_info, 0, sizeof (ts_info));
+
+ if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", name) >= sizeof (req.ifr_name)) {
+ close(sock_fd);
+ return 0;
+ }
+
+ if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
+ DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
+ close(sock_fd);
+ return 0;
+ }
+
+ if_index = req.ifr_ifindex;
+
+ ts_info.cmd = ETHTOOL_GET_TS_INFO;
+ req.ifr_data = (char *)&ts_info;
+
+ if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
+ DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
+ close(sock_fd);
+ return 0;
+ }
+
+ ts_config.flags = 0;
+ ts_config.tx_type = HWTSTAMP_TX_ON;
+ ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
+ req.ifr_data = (char *)&ts_config;
+
+ if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
+ DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
+ close(sock_fd);
+ return 0;
+ }
+
+ close(sock_fd);
+ phc_index = ts_info.phc_index;
+
+ if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
+ return 0;
+
+ phc_fd = open(phc_path, O_RDONLY);
+ if (phc_fd < 0) {
+ LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not open %s : %s", phc_path, strerror(errno));
+ return 0;
+ }
+
+ UTI_FdSetCloexec(phc_fd);
+
+ iface = ARR_GetNewElement(interfaces);
+
+ snprintf(iface->name, sizeof (iface->name), "%s", name);
+ iface->if_index = if_index;
+ iface->phc_fd = phc_fd;
+
+ /* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
+ iface->link_speed = 1000;
+ iface->l2_udp4_ntp_start = 42;
+ iface->l2_udp6_ntp_start = 62;
+
+ iface->clock = HCL_CreateInstance();
+
+ return 1;
+}
+
+/* ================================================== */
+
+static void
+update_interface_speed(struct Interface *iface)
+{
+ struct ethtool_cmd cmd;
+ struct ifreq req;
+ int sock_fd;
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ return;
+
+ memset(&req, 0, sizeof (req));
+ memset(&cmd, 0, sizeof (cmd));
+
+ snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface->name);
+ cmd.cmd = ETHTOOL_GSET;
+ req.ifr_data = (char *)&cmd;
+
+ if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
+ DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
+ close(sock_fd);
+ return;
+ }
+
+ close(sock_fd);
+
+ iface->link_speed = ethtool_cmd_speed(&cmd);
+}
+
+/* ================================================== */
+
+void
+NIO_Linux_Initialise(void)
+{
+ ARR_Instance config_hwts_ifaces;
+ char *if_name;
+ unsigned int i;
+
+ interfaces = ARR_CreateInstance(sizeof (struct Interface));
+
+ config_hwts_ifaces = CNF_GetHwTsInterfaces();
+
+ /* Enable HW timestamping on all specified interfaces. If no interface was
+ specified, use SW timestamping. */
+ if (ARR_GetSize(config_hwts_ifaces)) {
+ for (i = 0; i < ARR_GetSize(config_hwts_ifaces); i++) {
+ if_name = *(char **)ARR_GetElement(config_hwts_ifaces, i);
+ if (!add_interface(if_name))
+ LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", if_name);
+ }
+ ts_flags = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
+ ts_tx_flags = SOF_TIMESTAMPING_TX_HARDWARE;
+ } else {
+ ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
+ ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
+ }
+
+ /* Enable IP_PKTINFO in messages looped back to the error queue */
+ ts_flags |= SOF_TIMESTAMPING_OPT_CMSG;
+
+ /* Kernels before 4.7 ignore timestamping flags set in control messages */
+ permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
+}
+
+/* ================================================== */
+
+void
+NIO_Linux_Finalise(void)
+{
+ struct Interface *iface;
+ unsigned int i;
+
+ for (i = 0; i < ARR_GetSize(interfaces); i++) {
+ iface = ARR_GetElement(interfaces, i);
+ HCL_DestroyInstance(iface->clock);
+ close(iface->phc_fd);
+ }
+
+ ARR_DestroyInstance(interfaces);
+}
+
+/* ================================================== */
+
+int
+NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
+{
+ int val, flags;
+
+ if (!ts_flags)
+ return 0;
+
+ /* Enable SCM_TIMESTAMPING control messages and the socket's error queue in
+ order to receive our transmitted packets with more accurate timestamps */
+
+ val = 1;
+ flags = ts_flags;
+
+ if (client_only || permanent_ts_options)
+ flags |= ts_tx_flags;
+
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
+ LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
+ ts_flags = 0;
+ return 0;
+ }
+
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
+ LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_TIMESTAMPING");
+ ts_flags = 0;
+ return 0;
+ }
+
+ *events |= SCH_FILE_EXCEPTION;
+ return 1;
+}
+
+/* ================================================== */
+
+static int
+get_phc_sample(int phc_fd, struct timespec *phc_ts, struct timespec *local_ts, double *p_delay)
+{
+ struct ptp_sys_offset sys_off;
+ struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
+ double min_delay = 0.0, delays[PHC_READINGS], phc_sum, local_sum, local_prec;
+ int i, n;
+
+ /* Silence valgrind */
+ memset(&sys_off, 0, sizeof (sys_off));
+
+ sys_off.n_samples = PHC_READINGS;
+
+ if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
+ DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
+ return 0;
+ }
+
+ for (i = 0; i < PHC_READINGS; i++) {
+ ts1.tv_sec = sys_off.ts[i * 2].sec;
+ ts1.tv_nsec = sys_off.ts[i * 2].nsec;
+ ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
+ ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
+ ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
+ ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
+
+ sys_tss[i] = ts1;
+ phc_tss[i] = ts2;
+ delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
+
+ if (delays[i] <= 0.0)
+ /* Step in the middle of a PHC reading? */
+ return 0;
+
+ if (!i || delays[i] < min_delay)
+ min_delay = delays[i];
+ }
+
+ local_prec = LCL_GetSysPrecisionAsQuantum();
+
+ /* Combine best readings */
+ for (i = n = 0, phc_sum = local_sum = 0.0; i < PHC_READINGS; i++) {
+ if (delays[i] > min_delay + local_prec)
+ continue;
+
+ phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
+ local_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
+ n++;
+ }
+
+ assert(n);
+
+ UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
+ UTI_AddDoubleToTimespec(&sys_tss[0], local_sum / n, &ts1);
+ LCL_CookTime(&ts1, local_ts, NULL);
+ *p_delay = min_delay;
+
+ return 1;
+}
+
+/* ================================================== */
+
+static struct Interface *
+get_interface(int if_index)
+{
+ struct Interface *iface;
+ unsigned int i;
+
+ for (i = 0; i < ARR_GetSize(interfaces); i++) {
+ iface = ARR_GetElement(interfaces, i);
+ if (iface->if_index != if_index)
+ continue;
+
+ return iface;
+ }
+
+ return NULL;
+}
+
+/* ================================================== */
+
+static void
+process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
+ NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family)
+{
+ struct timespec sample_phc_ts, sample_local_ts;
+ double sample_delay, rx_correction;
+ int l2_length;
+
+ if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
+ if (!get_phc_sample(iface->phc_fd, &sample_phc_ts, &sample_local_ts, &sample_delay))
+ return;
+
+ HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
+ sample_delay / 2.0);
+
+ update_interface_speed(iface);
+ }
+
+ /* We need to transpose RX timestamps as hardware timestamps are normally
+ preamble timestamps and RX timestamps in NTP are supposed to be trailer
+ timestamps. Without raw sockets we don't know the length of the packet
+ at layer 2, so we make an assumption that UDP data start at the same
+ position as in the last transmitted packet which had a HW TX timestamp. */
+ if (rx_ntp_length && iface->link_speed) {
+ l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
+ iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
+ rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
+
+ UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
+ }
+
+ if (!HCL_CookTime(iface->clock, hw_ts, &local_ts->ts, &local_ts->err))
+ return;
+
+ local_ts->source = NTP_TS_HARDWARE;
+}
+
+/* ================================================== */
+/* Extract UDP data from a layer 2 message. Supported is Ethernet
+ with optional VLAN tags. */
+
+static int
+extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
+{
+ unsigned char *msg_start = msg;
+ union sockaddr_in46 addr;
+
+ remote_addr->ip_addr.family = IPADDR_UNSPEC;
+ remote_addr->port = 0;
+
+ /* Skip MACs */
+ if (len < 12)
+ return 0;
+ len -= 12, msg += 12;
+
+ /* Skip VLAN tag(s) if present */
+ while (len >= 4 && msg[0] == 0x81 && msg[1] == 0x00)
+ len -= 4, msg += 4;
+
+ /* Skip IPv4 or IPv6 ethertype */
+ if (len < 2 || !((msg[0] == 0x08 && msg[1] == 0x00) ||
+ (msg[0] == 0x86 && msg[1] == 0xdd)))
+ return 0;
+ len -= 2, msg += 2;
+
+ /* Parse destination address and port from IPv4/IPv6 and UDP headers */
+ if (len >= 20 && msg[0] >> 4 == 4) {
+ int ihl = (msg[0] & 0xf) * 4;
+
+ if (len < ihl + 8 || msg[9] != 17)
+ return 0;
+
+ memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t));
+ addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2);
+ addr.in4.sin_family = AF_INET;
+ len -= ihl + 8, msg += ihl + 8;
+#ifdef FEAT_IPV6
+ } else if (len >= 48 && msg[0] >> 4 == 6) {
+ /* IPv6 extension headers are not supported */
+ if (msg[6] != 17)
+ return 0;
+
+ memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
+ addr.in6.sin6_port = *(uint16_t *)(msg + 40 + 2);
+ addr.in6.sin6_family = AF_INET6;
+ len -= 48, msg += 48;
+#endif
+ } else {
+ return 0;
+ }
+
+ UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port);
+
+ /* Move the message to fix alignment of its fields */
+ if (len > 0)
+ memmove(msg_start, msg, len);
+
+ return len;
+}
+
+/* ================================================== */
+
+int
+NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *local_ts, struct msghdr *hdr,
+ int length, int sock_fd, int if_index)
+{
+ struct Interface *iface;
+ struct cmsghdr *cmsg;
+ int is_tx, l2_length;
+
+ is_tx = hdr->msg_flags & MSG_ERRQUEUE;
+ iface = NULL;
+
+ for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
+ struct scm_timestamping ts3;
+
+ memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
+
+ if (!UTI_IsZeroTimespec(&ts3.ts[0])) {
+ LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
+ local_ts->source = NTP_TS_KERNEL;
+ } else {
+ iface = get_interface(if_index);
+ if (iface) {
+ process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
+ remote_addr->ip_addr.family);
+ } else {
+ DEBUG_LOG(LOGF_NtpIOLinux, "HW clock not found for interface %d", if_index);
+ }
+ }
+ }
+
+ if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
+ (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
+ struct sock_extended_err err;
+
+ memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
+
+ if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
+ err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
+ DEBUG_LOG(LOGF_NtpIOLinux, "Unknown extended error");
+ /* Drop the message */
+ return 1;
+ }
+ }
+ }
+
+ /* Return the message if it's not received from the error queue */
+ if (!is_tx)
+ return 0;
+
+ /* The data from the error queue includes all layers up to UDP. We have to
+ extract the UDP data and also the destination address with port as there
+ currently doesn't seem to be a better way to get them both. */
+ l2_length = length;
+ length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
+
+ DEBUG_LOG(LOGF_NtpIOLinux, "Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
+ l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
+ sock_fd, if_index, local_ts->source);
+
+ /* Update assumed position of UDP data at layer 2 for next received packet */
+ if (iface && length) {
+ if (remote_addr->ip_addr.family == IPADDR_INET4)
+ iface->l2_udp4_ntp_start = l2_length - length;
+ else if (remote_addr->ip_addr.family == IPADDR_INET6)
+ iface->l2_udp6_ntp_start = l2_length - length;
+ }
+
+ /* Drop the message if HW timestamp is missing or its processing failed */
+ if ((ts_flags & SOF_TIMESTAMPING_RAW_HARDWARE) && local_ts->source != NTP_TS_HARDWARE) {
+ DEBUG_LOG(LOGF_NtpIOLinux, "Missing HW timestamp");
+ return 1;
+ }
+
+ if (length < NTP_NORMAL_PACKET_LENGTH)
+ return 1;
+
+ NSR_ProcessTx(remote_addr, local_addr, local_ts,
+ (NTP_Packet *)hdr->msg_iov[0].iov_base, length);
+
+ return 1;
+}
+
+/* ================================================== */
+
+int
+NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
+{
+ struct cmsghdr *cmsg;
+
+ /* Check if TX timestamping is disabled on this socket */
+ if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
+ return cmsglen;
+
+ /* Add control message that will enable TX timestamping for this message.
+ Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
+ control messages. */
+ cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen);
+ memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
+ cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
+
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SO_TIMESTAMPING;
+ cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags));
+
+ memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags));
+
+ return cmsglen;
+}
diff --git a/ntp_io_linux.h b/ntp_io_linux.h
new file mode 100644
index 0000000..db140ba
--- /dev/null
+++ b/ntp_io_linux.h
@@ -0,0 +1,37 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This is the header file for the Linux-specific NTP socket I/O bits.
+ */
+
+extern void NIO_Linux_Initialise(void);
+
+extern void NIO_Linux_Finalise(void);
+
+extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
+
+extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length,
+ int sock_fd, int if_index);
+
+extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
diff --git a/ntp_signd.c b/ntp_signd.c
new file mode 100644
index 0000000..1469668
--- /dev/null
+++ b/ntp_signd.c
@@ -0,0 +1,380 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Support for MS-SNTP authentication in Samba (ntp_signd)
+ */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "array.h"
+#include "conf.h"
+#include "logging.h"
+#include "ntp_io.h"
+#include "ntp_signd.h"
+#include "sched.h"
+#include "util.h"
+
+/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
+
+#define SIGND_VERSION 0
+
+typedef enum {
+ SIGN_TO_CLIENT = 0,
+ ASK_SERVER_TO_SIGN = 1,
+ CHECK_SERVER_SIGNATURE = 2,
+ SIGNING_SUCCESS = 3,
+ SIGNING_FAILURE = 4,
+} SigndOp;
+
+typedef struct {
+ uint32_t length;
+ uint32_t version;
+ uint32_t op;
+ uint16_t packet_id;
+ uint16_t _pad;
+ uint32_t key_id;
+ NTP_Packet packet_to_sign;
+} SigndRequest;
+
+typedef struct {
+ uint32_t length;
+ uint32_t version;
+ uint32_t op;
+ uint32_t packet_id;
+ NTP_Packet signed_packet;
+} SigndResponse;
+
+typedef struct {
+ NTP_Remote_Address remote_addr;
+ NTP_Local_Address local_addr;
+
+ int sent;
+ int received;
+ int request_length;
+ struct timespec request_ts;
+ SigndRequest request;
+ SigndResponse response;
+} SignInstance;
+
+/* As the communication with ntp_signd is asynchronous, incoming packets are
+ saved in a queue in order to avoid loss when they come in bursts */
+
+#define MAX_QUEUE_LENGTH 16U
+#define NEXT_QUEUE_INDEX(index) (((index) + 1) % MAX_QUEUE_LENGTH)
+#define IS_QUEUE_EMPTY() (queue_head == queue_tail)
+
+/* Fixed-size array of SignInstance */
+static ARR_Instance queue;
+static unsigned int queue_head;
+static unsigned int queue_tail;
+
+#define INVALID_SOCK_FD -1
+
+/* Unix domain socket connected to ntp_signd */
+static int sock_fd;
+
+#define MIN_AUTH_DELAY 1.0e-5
+#define MAX_AUTH_DELAY 1.0e-2
+
+/* Average time needed for signing one packet. This is used to adjust the
+ transmit timestamp in NTP packets. The timestamp won't be very accurate as
+ the delay is variable, but it should be good enough for MS-SNTP clients. */
+static double auth_delay;
+
+/* Flag indicating if the MS-SNTP authentication is enabled */
+static int enabled;
+
+/* ================================================== */
+
+static void read_write_socket(int sock_fd, int event, void *anything);
+
+/* ================================================== */
+
+static void
+close_socket(void)
+{
+ SCH_RemoveFileHandler(sock_fd);
+ close(sock_fd);
+ sock_fd = INVALID_SOCK_FD;
+
+ /* Empty the queue */
+ queue_head = queue_tail = 0;
+}
+
+/* ================================================== */
+
+static int
+open_socket(void)
+{
+ struct sockaddr_un s;
+
+ if (sock_fd >= 0)
+ return 1;
+
+ sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock_fd < 0) {
+ DEBUG_LOG(LOGF_NtpSignd, "Could not open signd socket : %s", strerror(errno));
+ return 0;
+ }
+
+ UTI_FdSetCloexec(sock_fd);
+ SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
+
+ s.sun_family = AF_UNIX;
+ if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
+ CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
+ DEBUG_LOG(LOGF_NtpSignd, "signd socket path too long");
+ close_socket();
+ return 0;
+ }
+
+ if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
+ DEBUG_LOG(LOGF_NtpSignd, "Could not connect to signd : %s", strerror(errno));
+ close_socket();
+ return 0;
+ }
+
+ DEBUG_LOG(LOGF_NtpSignd, "Connected to signd");
+
+ return 1;
+}
+
+/* ================================================== */
+
+static void
+process_response(SignInstance *inst)
+{
+ struct timespec ts;
+ double delay;
+
+ if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
+ DEBUG_LOG(LOGF_NtpSignd, "Invalid response ID");
+ return;
+ }
+
+ if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
+ DEBUG_LOG(LOGF_NtpSignd, "Signing failed");
+ return;
+ }
+
+ /* Check if the file descriptor is still valid */
+ if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
+ DEBUG_LOG(LOGF_NtpSignd, "Invalid NTP socket");
+ return;
+ }
+
+ SCH_GetLastEventTime(NULL, NULL, &ts);
+ delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
+
+ DEBUG_LOG(LOGF_NtpSignd, "Signing succeeded (delay %f)", delay);
+
+ /* Send the signed NTP packet */
+ NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
+ ntohl(inst->response.length) + sizeof (inst->response.length) -
+ offsetof(SigndResponse, signed_packet), 0);
+
+ /* Update exponential moving average of the authentication delay */
+ delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
+ auth_delay += 0.1 * (delay - auth_delay);
+}
+
+/* ================================================== */
+
+static void
+read_write_socket(int sock_fd, int event, void *anything)
+{
+ SignInstance *inst;
+ uint32_t response_length;
+ int s;
+
+ inst = ARR_GetElement(queue, queue_head);
+
+ if (event == SCH_FILE_OUTPUT) {
+ assert(!IS_QUEUE_EMPTY());
+ assert(inst->sent < inst->request_length);
+
+ if (!inst->sent)
+ SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
+
+ s = send(sock_fd, (char *)&inst->request + inst->sent,
+ inst->request_length - inst->sent, 0);
+
+ if (s < 0) {
+ DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
+ close_socket();
+ return;
+ }
+
+ DEBUG_LOG(LOGF_NtpSignd, "Sent %d bytes to signd", s);
+ inst->sent += s;
+
+ /* Try again later if the request is not complete yet */
+ if (inst->sent < inst->request_length)
+ return;
+
+ /* Disable output and wait for a response */
+ SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
+ }
+
+ if (event == SCH_FILE_INPUT) {
+ if (IS_QUEUE_EMPTY()) {
+ DEBUG_LOG(LOGF_NtpSignd, "Unexpected signd response");
+ close_socket();
+ return;
+ }
+
+ assert(inst->received < sizeof (inst->response));
+ s = recv(sock_fd, (char *)&inst->response + inst->received,
+ sizeof (inst->response) - inst->received, 0);
+
+ if (s <= 0) {
+ if (s < 0)
+ DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
+ else
+ DEBUG_LOG(LOGF_NtpSignd, "signd socket closed");
+
+ close_socket();
+ return;
+ }
+
+ DEBUG_LOG(LOGF_NtpSignd, "Received %d bytes from signd", s);
+ inst->received += s;
+
+ if (inst->received < sizeof (inst->response.length))
+ return;
+
+ response_length = ntohl(inst->response.length) + sizeof (inst->response.length);
+
+ if (response_length < offsetof(SigndResponse, signed_packet) ||
+ response_length > sizeof (SigndResponse)) {
+ DEBUG_LOG(LOGF_NtpSignd, "Invalid response length");
+ close_socket();
+ return;
+ }
+
+ /* Wait for more data if not complete yet */
+ if (inst->received < response_length)
+ return;
+
+ process_response(inst);
+
+ /* Move the head and enable output for the next packet */
+ queue_head = NEXT_QUEUE_INDEX(queue_head);
+ if (!IS_QUEUE_EMPTY())
+ SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
+ }
+}
+
+/* ================================================== */
+
+void
+NSD_Initialise()
+{
+ sock_fd = INVALID_SOCK_FD;
+ auth_delay = MIN_AUTH_DELAY;
+ enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
+
+ if (!enabled)
+ return;
+
+ queue = ARR_CreateInstance(sizeof (SignInstance));
+ ARR_SetSize(queue, MAX_QUEUE_LENGTH);
+ queue_head = queue_tail = 0;
+
+ LOG(LOGS_INFO, LOGF_NtpSignd, "MS-SNTP authentication enabled");
+}
+
+/* ================================================== */
+
+void
+NSD_Finalise()
+{
+ if (!enabled)
+ return;
+ if (sock_fd != INVALID_SOCK_FD)
+ close_socket();
+ ARR_DestroyInstance(queue);
+}
+
+/* ================================================== */
+
+extern int NSD_GetAuthDelay(uint32_t key_id)
+{
+ return 1.0e9 * auth_delay;
+}
+
+/* ================================================== */
+
+int
+NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
+{
+ SignInstance *inst;
+
+ if (!enabled) {
+ DEBUG_LOG(LOGF_NtpSignd, "signd disabled");
+ return 0;
+ }
+
+ if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
+ DEBUG_LOG(LOGF_NtpSignd, "signd queue full");
+ return 0;
+ }
+
+ if (length != NTP_NORMAL_PACKET_LENGTH) {
+ DEBUG_LOG(LOGF_NtpSignd, "Invalid packet length");
+ return 0;
+ }
+
+ if (!open_socket())
+ return 0;
+
+ inst = ARR_GetElement(queue, queue_tail);
+ inst->remote_addr = *remote_addr;
+ inst->local_addr = *local_addr;
+ inst->sent = 0;
+ inst->received = 0;
+ inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
+
+ /* The length field doesn't include itself */
+ inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
+ inst->request.version = htonl(SIGND_VERSION);
+ inst->request.op = htonl(SIGN_TO_CLIENT);
+ inst->request.packet_id = htons(queue_tail);
+ inst->request._pad = 0;
+ inst->request.key_id = htonl(key_id);
+
+ memcpy(&inst->request.packet_to_sign, packet, length);
+
+ /* Enable output if there was no pending request */
+ if (IS_QUEUE_EMPTY())
+ SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
+
+ queue_tail = NEXT_QUEUE_INDEX(queue_tail);
+
+ DEBUG_LOG(LOGF_NtpSignd, "Packet added to signd queue (%u:%u)",
+ queue_head, queue_tail);
+
+ return 1;
+}
diff --git a/ntp_signd.h b/ntp_signd.h
new file mode 100644
index 0000000..f45a5cb
--- /dev/null
+++ b/ntp_signd.h
@@ -0,0 +1,44 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header for MS-SNTP authentication via Samba (ntp_signd) */
+
+#ifndef GOT_NTP_SIGND_H
+#define GOT_NTP_SIGND_H
+
+#include "addressing.h"
+#include "ntp.h"
+
+/* Initialisation function */
+extern void NSD_Initialise(void);
+
+/* Finalisation function */
+extern void NSD_Finalise(void);
+
+/* Function to get an estimate of delay due to signing */
+extern int NSD_GetAuthDelay(uint32_t key_id);
+
+/* Function to sign an NTP packet and send it */
+extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
+
+#endif
diff --git a/ntp_sources.c b/ntp_sources.c
index dbd4748..5024a42 100644
--- a/ntp_sources.c
+++ b/ntp_sources.c
@@ -117,8 +117,8 @@ static void rehash_records(void);
static void clean_source_record(SourceRecord *record);
static void
-slew_sources(struct timeval *raw,
- struct timeval *cooked,
+slew_sources(struct timespec *raw,
+ struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -680,8 +680,8 @@ resolve_source_replacement(SourceRecord *record)
void
NSR_HandleBadSource(IPAddr *address)
{
- static struct timeval last_replacement;
- struct timeval now;
+ static struct timespec last_replacement;
+ struct timespec now;
NTP_Remote_Address remote_addr;
SourceRecord *record;
int slot, found;
@@ -702,7 +702,7 @@ NSR_HandleBadSource(IPAddr *address)
/* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now);
- UTI_DiffTimevalsToDouble(&diff, &now, &last_replacement);
+ diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG(LOGF_NtpSources, "replacement postponed");
return;
@@ -776,7 +776,8 @@ NSR_GetLocalRefid(IPAddr *address)
/* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */
void
-NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
+NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
SourceRecord *record;
struct SourcePool *pool;
@@ -788,11 +789,11 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
if (found == 2) { /* Must match IP address AND port number */
record = get_record(slot);
- if (!NCR_ProcessKnown(message, now, now_err, record->data, local_addr, length))
+ if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
return;
if (record->tentative) {
- /* This was the first valid reply from the source */
+ /* This was the first good reply from the source */
record->tentative = 0;
if (record->pool != INVALID_POOL) {
@@ -809,15 +810,34 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
}
}
} else {
- NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length);
+ NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
+ }
+}
+
+/* ================================================== */
+
+void
+NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
+{
+ SourceRecord *record;
+ int slot, found;
+
+ find_slot(remote_addr, &slot, &found);
+
+ if (found == 2) { /* Must match IP address AND port number */
+ record = get_record(slot);
+ NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
+ } else {
+ NCR_ProcessTxUnknown(remote_addr, local_addr, tx_ts, message, length);
}
}
/* ================================================== */
static void
-slew_sources(struct timeval *raw,
- struct timeval *cooked,
+slew_sources(struct timespec *raw,
+ struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -831,6 +851,7 @@ slew_sources(struct timeval *raw,
if (record->remote_addr) {
if (change_type == LCL_ChangeUnknownStep) {
NCR_ResetInstance(record->data);
+ NCR_ResetPoll(record->data);
} else {
NCR_SlewTimes(record->data, cooked, dfreq, doffset);
}
@@ -1083,7 +1104,7 @@ NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
identify the source record. */
void
-NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
+NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
NTP_Remote_Address rem_addr;
int slot, found;
@@ -1100,6 +1121,26 @@ NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
}
/* ================================================== */
+/* The ip address is assumed to be completed on input, that is how we
+ identify the source record. */
+
+int
+NSR_GetNTPReport(RPT_NTPReport *report)
+{
+ NTP_Remote_Address rem_addr;
+ int slot, found;
+
+ rem_addr.ip_addr = report->remote_addr;
+ rem_addr.port = 0;
+ find_slot(&rem_addr, &slot, &found);
+ if (!found)
+ return 0;
+
+ NCR_GetNTPReport(get_record(slot)->data, report);
+ return 1;
+}
+
+/* ================================================== */
void
NSR_GetActivityReport(RPT_ActivityReport *report)
diff --git a/ntp_sources.h b/ntp_sources.h
index 4d9bbbe..23e9612 100644
--- a/ntp_sources.h
+++ b/ntp_sources.h
@@ -87,7 +87,13 @@ extern void NSR_RefreshAddresses(void);
extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */
-extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
+extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
+
+/* This routine is called by ntp_io when a packet was sent to the network and
+ an accurate transmit timestamp was captured */
+extern void NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Initialisation function */
extern void NSR_Initialise(void);
@@ -121,7 +127,9 @@ extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
-extern void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now);
+extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
+
+extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report);
diff --git a/pktlength.c b/pktlength.c
index 7366e51..84facb6 100644
--- a/pktlength.c
+++ b/pktlength.c
@@ -82,8 +82,8 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENYALL */
REQ_LENGTH_ENTRY(ac_check, null), /* ACCHECK */
REQ_LENGTH_ENTRY(ac_check, null), /* CMDACCHECK */
- REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER */
- REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER */
+ { 0, 0 }, /* ADD_SERVER */
+ { 0, 0 }, /* ADD_PEER */
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */
@@ -113,6 +113,9 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
+ REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
+ REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER2 */
+ REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER2 */
};
static const uint16_t reply_lengths[] = {
@@ -132,6 +135,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
+ RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
};
/* ================================================== */
diff --git a/refclock.c b/refclock.c
index 862a624..4686af0 100644
--- a/refclock.c
+++ b/refclock.c
@@ -48,7 +48,7 @@ extern RefclockDriver RCL_PHC_driver;
struct FilterSample {
double offset;
double dispersion;
- struct timeval sample_time;
+ struct timespec sample_time;
};
struct MedianFilter {
@@ -77,6 +77,7 @@ struct RCL_Instance_Record {
int leap_status;
int pps_rate;
int pps_active;
+ int max_lock_age;
struct MedianFilter filter;
uint32_t ref_id;
uint32_t lock_ref;
@@ -92,24 +93,24 @@ static ARR_Instance refclocks;
static LOG_FileID logfileid;
-static int valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked);
-static int pps_stratum(RCL_Instance instance, struct timeval *tv);
+static int valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked);
+static int pps_stratum(RCL_Instance instance, struct timespec *ts);
static void poll_timeout(void *arg);
-static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
+static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything);
-static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
+static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
static void filter_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 timeval *sample_time, double offset, double dispersion);
-static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
+static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion);
+static int filter_get_last_sample(struct MedianFilter *filter, struct 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 timeval *sample_time, double *offset, double *dispersion);
-static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
+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
@@ -202,6 +203,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->leap_status = LEAP_Normal;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
+ inst->max_lock_age = params->max_lock_age;
inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset;
inst->delay = params->delay;
@@ -300,7 +302,7 @@ RCL_StartRefclocks(void)
}
void
-RCL_ReportSource(RPT_SourceReport *report, struct timeval *now)
+RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
unsigned int i;
uint32_t ref_id;
@@ -362,13 +364,13 @@ RCL_GetDriverOption(RCL_Instance instance, char *name)
}
int
-RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap)
+RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
double correction, dispersion;
- struct timeval cooked_time;
+ struct timespec cooked_time;
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
- UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time);
+ UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */
@@ -400,16 +402,16 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
}
int
-RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
+RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
{
double correction, dispersion, offset;
- struct timeval cooked_time;
+ struct timespec cooked_time;
int rate;
NTP_Leap leap;
leap = LEAP_Normal;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
- UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
+ UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
@@ -430,7 +432,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
if (instance->lock_ref != -1) {
RCL_Instance lock_refclock;
- struct timeval ref_sample_time;
+ struct timespec ref_sample_time;
double sample_diff, ref_offset, ref_dispersion, shift;
lock_refclock = get_refclock(instance->lock_ref);
@@ -443,8 +445,8 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
- UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time);
- if (fabs(sample_diff) >= 2.0 / rate) {
+ sample_diff = UTI_DiffTimespecsToDouble(&cooked_time, &ref_sample_time);
+ if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
sample_diff);
return 0;
@@ -469,7 +471,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff);
} else {
- struct timeval ref_time;
+ struct timespec ref_time;
int is_synchronised, stratum;
double root_delay, root_dispersion, distance;
uint32_t ref_id;
@@ -504,22 +506,22 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
}
static int
-valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked)
+valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked)
{
- struct timeval raw_time, last_sample_time;
+ struct timespec now_raw, last_sample_time;
double diff, last_offset, last_dispersion;
- LCL_ReadRawTime(&raw_time);
- UTI_DiffTimevalsToDouble(&diff, &raw_time, raw);
+ LCL_ReadRawTime(&now_raw);
+ diff = UTI_DiffTimespecsToDouble(&now_raw, raw);
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_CompareTimevals(&last_sample_time, cooked) >= 0)) {
+ UTI_CompareTimespecs(&last_sample_time, cooked) >= 0)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f raw=%s cooked=%s",
UTI_RefidToString(instance->ref_id), diff,
- UTI_TimevalToString(raw), UTI_TimevalToString(cooked));
+ UTI_TimespecToString(raw), UTI_TimespecToString(cooked));
return 0;
}
@@ -527,9 +529,9 @@ valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *co
}
static int
-pps_stratum(RCL_Instance instance, struct timeval *tv)
+pps_stratum(RCL_Instance instance, struct timespec *ts)
{
- struct timeval ref_time;
+ struct timespec ref_time;
int is_synchronised, stratum;
unsigned int i;
double root_delay, root_dispersion;
@@ -537,7 +539,7 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
uint32_t ref_id;
RCL_Instance refclock;
- REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum,
+ REF_GetReferenceParams(ts, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
/* Don't change our stratum if the local reference is active
@@ -574,7 +576,7 @@ poll_timeout(void *arg)
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion;
- struct timeval sample_time;
+ struct timespec sample_time;
int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
@@ -602,7 +604,7 @@ poll_timeout(void *arg)
}
static void
-slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
+slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
unsigned int i;
@@ -625,7 +627,7 @@ add_dispersion(double dispersion, void *anything)
}
static void
-log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
+log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
{
char sync_stats[4] = {'N', '+', '-', '?'};
@@ -635,7 +637,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
if (!filtered) {
LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec),
- (int)sample_time->tv_usec,
+ (int)sample_time->tv_nsec / 1000,
UTI_RefidToString(instance->ref_id),
instance->driver_polled,
sync_stats[instance->leap_status],
@@ -646,7 +648,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
} else {
LOG_FileWrite(logfileid, "%s.%06d %-5s - %1c - - %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec),
- (int)sample_time->tv_usec,
+ (int)sample_time->tv_nsec / 1000,
UTI_RefidToString(instance->ref_id),
sync_stats[instance->leap_status],
cooked_offset,
@@ -699,7 +701,7 @@ filter_get_avg_sample_dispersion(struct MedianFilter *filter)
}
static void
-filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion)
+filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
{
filter->index++;
filter->index %= filter->length;
@@ -712,11 +714,11 @@ filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
- filter->index, UTI_TimevalToString(sample_time), offset, dispersion);
+ filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
}
static int
-filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
+filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
if (filter->last < 0)
return 0;
@@ -835,7 +837,7 @@ filter_select_samples(struct MedianFilter *filter)
}
static int
-filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
+filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
struct FilterSample *s, *ls;
int i, n, dof;
@@ -852,7 +854,7 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
for (i = 0; i < n; i++) {
s = &filter->samples[filter->selected[i]];
- UTI_DiffTimevalsToDouble(&filter->x_data[i], &s->sample_time, &ls->sample_time);
+ filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
filter->y_data[i] = s->offset;
filter->w_data[i] = s->dispersion;
}
@@ -927,7 +929,7 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
if (d < e)
d = e;
- UTI_AddDoubleToTimeval(&ls->sample_time, x, sample_time);
+ UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
*offset = y;
*dispersion = d;
@@ -937,15 +939,26 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
}
static void
-filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset)
+filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
{
- int i;
+ int i, first, last;
double delta_time;
- struct timeval *sample;
+ struct timespec *sample;
- for (i = 0; i < filter->used; i++) {
+ if (filter->last < 0)
+ return;
+
+ /* always slew the last sample as it may be needed by PPS refclocks */
+ if (filter->used > 0) {
+ first = 0;
+ last = filter->used - 1;
+ } else {
+ first = last = filter->last;
+ }
+
+ for (i = first; i <= last; i++) {
sample = &filter->samples[i].sample_time;
- UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
+ UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
filter->samples[i].offset -= delta_time;
}
}
diff --git a/refclock.h b/refclock.h
index e089992..7b76c23 100644
--- a/refclock.h
+++ b/refclock.h
@@ -41,6 +41,7 @@ typedef struct {
int min_samples;
int max_samples;
int sel_options;
+ int max_lock_age;
uint32_t ref_id;
uint32_t lock_ref_id;
double offset;
@@ -61,14 +62,14 @@ extern void RCL_Initialise(void);
extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void);
-extern void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now);
+extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
/* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
-extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap);
-extern int RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second);
+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);
#endif
diff --git a/refclock_phc.c b/refclock_phc.c
index deaec25..21ca9b5 100644
--- a/refclock_phc.c
+++ b/refclock_phc.c
@@ -56,11 +56,6 @@ struct phc_reading {
struct timespec sys_ts2;
};
-static double diff_ts(struct timespec *ts1, struct timespec *ts2)
-{
- return (ts1->tv_sec - ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
-}
-
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
{
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
@@ -145,7 +140,6 @@ static void phc_finalise(RCL_Instance instance)
static int phc_poll(RCL_Instance instance)
{
struct phc_reading readings[NUM_READINGS];
- struct timeval tv;
double offset = 0.0, delay, best_delay = 0.0;
int i, phc_fd, best;
@@ -163,7 +157,7 @@ static int phc_poll(RCL_Instance instance)
/* Find the fastest reading */
for (i = 0; i < NUM_READINGS; i++) {
- delay = diff_ts(&readings[i].sys_ts2, &readings[i].sys_ts1);
+ delay = UTI_DiffTimespecsToDouble(&readings[i].sys_ts2, &readings[i].sys_ts1);
if (!i || best_delay > delay) {
best = i;
@@ -171,13 +165,12 @@ static int phc_poll(RCL_Instance instance)
}
}
- offset = diff_ts(&readings[best].phc_ts, &readings[best].sys_ts2) + best_delay / 2.0;
- tv.tv_sec = readings[best].sys_ts2.tv_sec;
- tv.tv_usec = readings[best].sys_ts2.tv_nsec / 1000;
+ offset = UTI_DiffTimespecsToDouble(&readings[best].phc_ts, &readings[best].sys_ts2) +
+ best_delay / 2.0;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
- return RCL_AddSample(instance, &tv, offset, LEAP_Normal);
+ return RCL_AddSample(instance, &readings[best].sys_ts2, offset, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {
diff --git a/refclock_pps.c b/refclock_pps.c
index cd3cd83..79016de 100644
--- a/refclock_pps.c
+++ b/refclock_pps.c
@@ -124,7 +124,6 @@ static int pps_poll(RCL_Instance instance)
{
struct pps_instance *pps;
struct timespec ts;
- struct timeval tv;
pps_info_t pps_info;
pps_seq_t seq;
@@ -146,17 +145,15 @@ static int pps_poll(RCL_Instance instance)
ts = pps_info.clear_timestamp;
}
- if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
- DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%lu.%09lu",
- seq, ts.tv_sec, ts.tv_nsec);
+ if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
+ DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%s",
+ seq, UTI_TimespecToString(&ts));
return 0;
}
pps->last_seq = seq;
- tv.tv_sec = ts.tv_sec;
- tv.tv_usec = ts.tv_nsec / 1000;
- return RCL_AddPulse(instance, &tv, ts.tv_nsec / 1e9);
+ return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
}
RefclockDriver RCL_PPS_driver = {
diff --git a/refclock_shm.c b/refclock_shm.c
index 3ad0a6a..905121b 100644
--- a/refclock_shm.c
+++ b/refclock_shm.c
@@ -90,7 +90,7 @@ static void shm_finalise(RCL_Instance instance)
static int shm_poll(RCL_Instance instance)
{
- struct timeval tv;
+ struct timespec receive_ts, clock_ts;
struct shmTime t, *shm;
double offset;
@@ -107,17 +107,23 @@ static int shm_poll(RCL_Instance instance)
shm->valid = 0;
- tv.tv_sec = t.receiveTimeStampSec;
- tv.tv_usec = t.receiveTimeStampUSec;
+ receive_ts.tv_sec = t.receiveTimeStampSec;
+ clock_ts.tv_sec = t.clockTimeStampSec;
- offset = t.clockTimeStampSec - t.receiveTimeStampSec;
if (t.clockTimeStampNSec / 1000 == t.clockTimeStampUSec &&
- t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec)
- offset += (t.clockTimeStampNSec - t.receiveTimeStampNSec) * 1e-9;
- else
- offset += (t.clockTimeStampUSec - t.receiveTimeStampUSec) * 1e-6;
+ t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec) {
+ receive_ts.tv_nsec = t.receiveTimeStampNSec;
+ clock_ts.tv_nsec = t.clockTimeStampNSec;
+ } else {
+ receive_ts.tv_nsec = 1000 * t.receiveTimeStampUSec;
+ clock_ts.tv_nsec = 1000 * t.clockTimeStampUSec;
+ }
+
+ UTI_NormaliseTimespec(&clock_ts);
+ UTI_NormaliseTimespec(&receive_ts);
+ offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
- return RCL_AddSample(instance, &tv, offset, t.leap);
+ return RCL_AddSample(instance, &receive_ts, offset, t.leap);
}
RefclockDriver RCL_SHM_driver = {
diff --git a/refclock_sock.c b/refclock_sock.c
index 8ab4d52..6e71368 100644
--- a/refclock_sock.c
+++ b/refclock_sock.c
@@ -57,14 +57,14 @@ struct sock_sample {
int magic;
};
-static void read_sample(void *anything)
+static void read_sample(int sockfd, int event, void *anything)
{
struct sock_sample sample;
+ struct timespec ts;
RCL_Instance instance;
- int sockfd, s;
+ int s;
instance = (RCL_Instance)anything;
- sockfd = (long)RCL_GetDriverData(instance);
s = recv(sockfd, &sample, sizeof (sample), 0);
@@ -86,10 +86,13 @@ static void read_sample(void *anything)
return;
}
+ UTI_TimevalToTimespec(&sample.tv, &ts);
+ UTI_NormaliseTimespec(&ts);
+
if (sample.pulse) {
- RCL_AddPulse(instance, &sample.tv, sample.offset);
+ RCL_AddPulse(instance, &ts, sample.offset);
} else {
- RCL_AddSample(instance, &sample.tv, sample.offset, sample.leap);
+ RCL_AddSample(instance, &ts, sample.offset, sample.leap);
}
}
@@ -122,7 +125,7 @@ static int sock_initialise(RCL_Instance instance)
}
RCL_SetDriverData(instance, (void *)(long)sockfd);
- SCH_AddInputFileHandler(sockfd, read_sample, instance);
+ SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance);
return 1;
}
@@ -131,7 +134,7 @@ static void sock_finalise(RCL_Instance instance)
int sockfd;
sockfd = (long)RCL_GetDriverData(instance);
- SCH_RemoveInputFileHandler(sockfd);
+ SCH_RemoveFileHandler(sockfd);
close(sockfd);
}
diff --git a/reference.c b/reference.c
index 1700186..3fdcb38 100644
--- a/reference.c
+++ b/reference.c
@@ -52,7 +52,7 @@ static int our_leap_sec;
static int our_stratum;
static uint32_t our_ref_id;
static IPAddr our_ref_ip;
-struct timeval our_ref_time;
+static struct timespec our_ref_time;
static double our_skew;
static double our_residual_freq;
static double our_root_delay;
@@ -136,7 +136,7 @@ static int next_fb_drift;
static SCH_TimeoutID fb_drift_timeout_id;
/* Timestamp of last reference update */
-static struct timeval last_ref_update;
+static struct timespec last_ref_update;
static double last_ref_update_interval;
/* ================================================== */
@@ -147,23 +147,22 @@ static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */
static void
-handle_slew(struct timeval *raw,
- struct timeval *cooked,
+handle_slew(struct timespec *raw,
+ struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
void *anything)
{
double delta;
- struct timeval now;
+ struct timespec now;
- UTI_AdjustTimeval(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
+ UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
- last_ref_update.tv_sec = 0;
- last_ref_update.tv_usec = 0;
+ UTI_ZeroTimespec(&last_ref_update);
} else if (last_ref_update.tv_sec) {
- UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
+ UTI_AdjustTimespec(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
}
/* When the clock was stepped, check if that doesn't change our leap status
@@ -267,8 +266,7 @@ REF_Initialise(void)
fb_drift_timeout_id = 0;
}
- last_ref_update.tv_sec = 0;
- last_ref_update.tv_usec = 0;
+ UTI_ZeroTimespec(&last_ref_update);
last_ref_update_interval = 0.0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -468,16 +466,16 @@ fb_drift_timeout(void *arg)
/* ================================================== */
static void
-schedule_fb_drift(struct timeval *now)
+schedule_fb_drift(struct timespec *now)
{
int i, c, secs;
double unsynchronised;
- struct timeval when;
+ struct timespec when;
if (fb_drift_timeout_id)
return; /* already scheduled */
- UTI_DiffTimevalsToDouble(&unsynchronised, now, &last_ref_update);
+ unsynchronised = UTI_DiffTimespecsToDouble(now, &last_ref_update);
for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) {
secs = 1 << i;
@@ -499,7 +497,7 @@ schedule_fb_drift(struct timeval *now)
if (i <= fb_drift_max) {
next_fb_drift = i;
- UTI_AddDoubleToTimeval(now, secs - unsynchronised, &when);
+ UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i);
}
@@ -727,7 +725,7 @@ leap_start_timeout(void *arg)
static void
set_leap_timeout(time_t now)
{
- struct timeval when;
+ struct timespec when;
/* Stop old timer if there is one */
SCH_RemoveTimeout(leap_timeout_id);
@@ -741,12 +739,12 @@ set_leap_timeout(time_t now)
will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
- when.tv_usec = 0;
+ when.tv_nsec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
if (leap_mode == REF_LeapModeSystem) {
when.tv_sec--;
- when.tv_usec = 500000;
+ when.tv_nsec = 500000000;
}
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
@@ -804,7 +802,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
/* ================================================== */
static void
-write_log(struct timeval *ref_time, char *ref, int stratum, NTP_Leap leap,
+write_log(struct timespec *ref_time, char *ref, int stratum, NTP_Leap leap,
double freq, double skew, double offset, int combined_sources,
double offset_sd, double uncorrected_offset)
{
@@ -881,7 +879,7 @@ REF_SetReference(int stratum,
int combined_sources,
uint32_t ref_id,
IPAddr *ref_ip,
- struct timeval *ref_time,
+ struct timespec *ref_time,
double offset,
double offset_sd,
double frequency,
@@ -902,7 +900,8 @@ REF_SetReference(int stratum,
double elapsed;
double correction_rate;
double uncorrected_offset, accumulate_offset, step_offset;
- struct timeval now, raw_now;
+ struct timespec now, raw_now;
+ NTP_int64 ref_fuzz;
assert(initialised);
@@ -936,9 +935,9 @@ REF_SetReference(int stratum,
LCL_ReadRawTime(&raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
- UTI_AddDoubleToTimeval(&raw_now, uncorrected_offset, &now);
+ UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
- UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time);
+ elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
our_offset = offset + elapsed * frequency;
if (!is_offset_ok(our_offset))
@@ -956,7 +955,7 @@ REF_SetReference(int stratum,
our_root_dispersion = root_dispersion;
if (last_ref_update.tv_sec) {
- UTI_DiffTimevalsToDouble(&update_interval, &now, &last_ref_update);
+ update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
if (update_interval < 0.0)
update_interval = 0.0;
} else {
@@ -1043,6 +1042,15 @@ REF_SetReference(int stratum,
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
+ /* Add a random error of up to one second to the reference time to make it
+ less useful when disclosed to NTP and cmdmon clients for estimating
+ receive timestamps in the interleaved symmetric NTP mode */
+ UTI_GetNtp64Fuzz(&ref_fuzz, 0);
+ UTI_TimespecToNtp64(&our_ref_time, &ref_fuzz, &ref_fuzz);
+ UTI_Ntp64ToTimespec(&ref_fuzz, &our_ref_time);
+ if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
+ our_ref_time.tv_sec--;
+
abs_freq_ppm = LCL_ReadAbsoluteFrequency();
write_log(&now,
@@ -1089,7 +1097,7 @@ REF_SetReference(int stratum,
void
REF_SetManualReference
(
- struct timeval *ref_time,
+ struct timespec *ref_time,
double offset,
double frequency,
double skew
@@ -1108,7 +1116,7 @@ void
REF_SetUnsynchronised(void)
{
/* Variables required for logging to statistics log */
- struct timeval now, now_raw;
+ struct timespec now, now_raw;
double uncorrected_offset;
assert(initialised);
@@ -1121,7 +1129,7 @@ REF_SetUnsynchronised(void)
LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &uncorrected_offset, NULL);
- UTI_AddDoubleToTimeval(&now_raw, uncorrected_offset, &now);
+ UTI_AddDoubleToTimespec(&now_raw, uncorrected_offset, &now);
if (fb_drifts) {
schedule_fb_drift(&now);
@@ -1149,12 +1157,12 @@ REF_SetUnsynchronised(void)
void
REF_GetReferenceParams
(
- struct timeval *local_time,
+ struct timespec *local_time,
int *is_synchronised,
NTP_Leap *leap_status,
int *stratum,
uint32_t *ref_id,
- struct timeval *ref_time,
+ struct timespec *ref_time,
double *root_delay,
double *root_dispersion
)
@@ -1164,7 +1172,7 @@ REF_GetReferenceParams
assert(initialised);
if (are_we_synchronised) {
- UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
+ elapsed = UTI_DiffTimespecsToDouble(local_time, &our_ref_time);
dispersion = our_root_dispersion +
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
} else {
@@ -1215,7 +1223,7 @@ REF_GetReferenceParams
*leap_status = LEAP_Unsynchronised;
*stratum = NTP_MAX_STRATUM;
*ref_id = NTP_REFID_UNSYNC;
- ref_time->tv_sec = ref_time->tv_usec = 0;
+ UTI_ZeroTimespec(ref_time);
/* These values seem to be standard for a client, and
any peer or client of ours will ignore them anyway because
we don't claim to be synchronised */
@@ -1230,7 +1238,7 @@ REF_GetReferenceParams
int
REF_GetOurStratum(void)
{
- struct timeval now_cooked, ref_time;
+ struct timespec now_cooked, ref_time;
int synchronised, stratum;
NTP_Leap leap_status;
uint32_t ref_id;
@@ -1303,7 +1311,7 @@ REF_DisableLocal(void)
int REF_IsLeapSecondClose(void)
{
- struct timeval now, now_raw;
+ struct timespec now, now_raw;
time_t t;
if (!our_leap_sec)
@@ -1327,13 +1335,13 @@ int REF_IsLeapSecondClose(void)
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{
- struct timeval now_raw, now_cooked;
+ struct timespec now_raw, now_cooked;
double correction;
int synchronised;
LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &correction, NULL);
- UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked);
+ UTI_AddDoubleToTimespec(&now_raw, correction, &now_cooked);
REF_GetReferenceParams(&now_cooked, &synchronised,
&rep->leap_status, &rep->stratum,
diff --git a/reference.h b/reference.h
index e4387b0..e376770 100644
--- a/reference.h
+++ b/reference.h
@@ -99,12 +99,12 @@ extern REF_LeapMode REF_GetLeapMode(void);
extern void REF_GetReferenceParams
(
- struct timeval *local_time,
+ struct timespec *local_time,
int *is_synchronised,
NTP_Leap *leap,
int *stratum,
uint32_t *ref_id,
- struct timeval *ref_time,
+ struct timespec *ref_time,
double *root_delay,
double *root_dispersion
);
@@ -140,7 +140,7 @@ extern void REF_SetReference
int combined_sources,
uint32_t ref_id,
IPAddr *ref_ip,
- struct timeval *ref_time,
+ struct timespec *ref_time,
double offset,
double offset_sd,
double frequency,
@@ -151,7 +151,7 @@ extern void REF_SetReference
extern void REF_SetManualReference
(
- struct timeval *ref_time,
+ struct timespec *ref_time,
double offset,
double frequency,
double skew
diff --git a/regress.c b/regress.c
index 71845d5..bbc138a 100644
--- a/regress.c
+++ b/regress.c
@@ -109,7 +109,7 @@ double
RGR_GetTCoef(int dof)
{
/* Assuming now the 99.95% quantile */
- static double coefs[] =
+ static const float coefs[] =
{ 636.6, 31.6, 12.92, 8.61, 6.869,
5.959, 5.408, 5.041, 4.781, 4.587,
4.437, 4.318, 4.221, 4.140, 4.073,
@@ -132,7 +132,7 @@ RGR_GetTCoef(int dof)
double
RGR_GetChi2Coef(int dof)
{
- static double coefs[] = {
+ static const float coefs[] = {
2.706, 4.605, 6.251, 7.779, 9.236, 10.645, 12.017, 13.362,
14.684, 15.987, 17.275, 18.549, 19.812, 21.064, 22.307, 23.542,
24.769, 25.989, 27.204, 28.412, 29.615, 30.813, 32.007, 33.196,
@@ -151,20 +151,6 @@ RGR_GetChi2Coef(int dof)
}
/* ================================================== */
-/* Structure used for holding results of each regression */
-
-typedef struct {
- double variance;
- double slope_sd;
- double slope;
- double offset;
- double offset_sd;
- double K2; /* Variance / slope_var */
- int n; /* Number of points */
- int dof; /* Number of degrees of freedom */
-} RegressionResult;
-
-/* ================================================== */
/* Critical value for number of runs of residuals with same sign.
5% critical region for now. */
@@ -653,3 +639,57 @@ RGR_FindBestRobustRegression
}
/* ================================================== */
+/* This routine performs linear regression with two independent variables.
+ It returns non-zero status if there were enough data points and there
+ was a solution. */
+
+int
+RGR_MultipleRegress
+(double *x1, /* first independent variable */
+ double *x2, /* second independent variable */
+ double *y, /* measured data */
+
+ int n, /* number of data points */
+
+ /* The results */
+ double *b2 /* estimated second slope */
+ /* other values are not needed yet */
+)
+{
+ double Sx1, Sx2, Sx1x1, Sx1x2, Sx2x2, Sx1y, Sx2y, Sy;
+ double U, V, V1, V2, V3;
+ int i;
+
+ if (n < 4)
+ return 0;
+
+ Sx1 = Sx2 = Sx1x1 = Sx1x2 = Sx2x2 = Sx1y = Sx2y = Sy = 0.0;
+
+ for (i = 0; i < n; i++) {
+ Sx1 += x1[i];
+ Sx2 += x2[i];
+ Sx1x1 += x1[i] * x1[i];
+ Sx1x2 += x1[i] * x2[i];
+ Sx2x2 += x2[i] * x2[i];
+ Sx1y += x1[i] * y[i];
+ Sx2y += x2[i] * y[i];
+ Sy += y[i];
+ }
+
+ U = n * (Sx1x2 * Sx1y - Sx1x1 * Sx2y) +
+ Sx1 * Sx1 * Sx2y - Sx1 * Sx2 * Sx1y +
+ Sy * (Sx2 * Sx1x1 - Sx1 * Sx1x2);
+
+ V1 = n * (Sx1x2 * Sx1x2 - Sx1x1 * Sx2x2);
+ V2 = Sx1 * Sx1 * Sx2x2 + Sx2 * Sx2 * Sx1x1;
+ V3 = -2.0 * Sx1 * Sx2 * Sx1x2;
+ V = V1 + V2 + V3;
+
+ /* Check if there is a (numerically stable) solution */
+ if (fabs(V) * 1.0e10 <= -V1 + V2 + fabs(V3))
+ return 0;
+
+ *b2 = U / V;
+
+ return 1;
+}
diff --git a/regress.h b/regress.h
index 0dfce9a..4e4c32b 100644
--- a/regress.h
+++ b/regress.h
@@ -119,4 +119,16 @@ RGR_FindBestRobustRegression
int *n_runs,
int *best_start);
+int
+RGR_MultipleRegress
+(double *x1, /* first independent variable */
+ double *x2, /* second independent variable */
+ double *y, /* measured data */
+
+ int n, /* number of data points */
+
+ /* The results */
+ double *b2 /* estimated second slope */
+);
+
#endif /* GOT_REGRESS_H */
diff --git a/reports.h b/reports.h
index 7d5d431..6a24670 100644
--- a/reports.h
+++ b/reports.h
@@ -31,8 +31,6 @@
#include "addressing.h"
#include "ntp.h"
-#define REPORT_INVALID_OFFSET 0x80000000
-
typedef struct {
IPAddr ip_addr;
int stratum;
@@ -53,7 +51,7 @@ typedef struct {
IPAddr ip_addr;
int stratum;
NTP_Leap leap_status;
- struct timeval ref_time;
+ struct timespec ref_time;
double current_correction;
double last_offset;
double rms_offset;
@@ -79,7 +77,7 @@ typedef struct {
} RPT_SourcestatsReport;
typedef struct {
- struct timeval ref_time;
+ struct timespec ref_time;
unsigned short n_samples;
unsigned short n_runs;
unsigned long span_seconds;
@@ -109,7 +107,7 @@ typedef struct {
} RPT_ServerStatsReport;
typedef struct {
- struct timeval when;
+ struct timespec when;
double slewed_offset;
double orig_offset;
double residual;
@@ -133,4 +131,33 @@ typedef struct {
double remaining_time;
} RPT_SmoothingReport;
+typedef struct {
+ IPAddr remote_addr;
+ IPAddr local_addr;
+ uint16_t remote_port;
+ uint8_t leap;
+ uint8_t version;
+ uint8_t mode;
+ uint8_t stratum;
+ int8_t poll;
+ int8_t precision;
+ double root_delay;
+ double root_dispersion;
+ uint32_t ref_id;
+ struct timespec ref_time;
+ double offset;
+ double peer_delay;
+ double peer_dispersion;
+ double response_time;
+ double jitter_asymmetry;
+ uint16_t tests;
+ int interleaved;
+ int authenticated;
+ char tx_tss_char;
+ char rx_tss_char;
+ uint32_t total_tx_count;
+ uint32_t total_rx_count;
+ uint32_t total_valid_count;
+} RPT_NTPReport;
+
#endif /* GOT_REPORTS_H */
diff --git a/rtc.c b/rtc.c
index 8d076c6..05fcad7 100644
--- a/rtc.c
+++ b/rtc.c
@@ -98,7 +98,7 @@ get_driftfile_time(void)
static void
apply_driftfile_time(time_t t)
{
- struct timeval now;
+ struct timespec now;
LCL_ReadCookedTime(&now, NULL);
diff --git a/rtc_linux.c b/rtc_linux.c
index ee915d3..8d73102 100644
--- a/rtc_linux.c
+++ b/rtc_linux.c
@@ -50,7 +50,7 @@
static void measurement_timeout(void *any);
-static void read_from_device(void *any);
+static void read_from_device(int fd_, int event, void *any);
/* ================================================== */
@@ -92,9 +92,8 @@ static double *rtc_trim = NULL;
static time_t rtc_ref;
-/* System clock (gettimeofday) samples associated with the above
- samples. */
-static struct timeval *system_times = NULL;
+/* System clock samples associated with the above samples. */
+static struct timespec *system_times = NULL;
/* Number of samples currently stored. */
static int n_samples;
@@ -170,7 +169,7 @@ discard_samples(int new_first)
memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
- memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timeval));
+ memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
n_samples = n_to_save;
}
@@ -180,7 +179,7 @@ discard_samples(int new_first)
#define NEW_FIRST_WHEN_FULL 4
static void
-accumulate_sample(time_t rtc, struct timeval *sys)
+accumulate_sample(time_t rtc, struct timespec *sys)
{
if (n_samples == MAX_SAMPLES) {
@@ -225,7 +224,7 @@ run_regression(int new_sample,
for (i=0; i<n_samples; i++) {
rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref);
offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
- (1.0e-6 * (double) system_times[i].tv_usec) +
+ (1.0e-9 * system_times[i].tv_nsec) +
rtc_rel[i]);
}
@@ -262,7 +261,7 @@ run_regression(int new_sample,
static void
slew_samples
-(struct timeval *raw, struct timeval *cooked,
+(struct timespec *raw, struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -278,7 +277,7 @@ slew_samples
}
for (i=0; i<n_samples; i++) {
- UTI_AdjustTimeval(system_times + i, cooked, system_times + i, &delta_time,
+ UTI_AdjustTimespec(system_times + i, cooked, system_times + i, &delta_time,
dfreq, doffset);
}
@@ -534,7 +533,7 @@ RTC_Linux_Initialise(void)
{
rtc_sec = MallocArray(time_t, MAX_SAMPLES);
rtc_trim = MallocArray(double, MAX_SAMPLES);
- system_times = MallocArray(struct timeval, MAX_SAMPLES);
+ system_times = MallocArray(struct timespec, MAX_SAMPLES);
/* Setup details depending on configuration options */
setup_config();
@@ -564,7 +563,7 @@ RTC_Linux_Initialise(void)
operating_mode = OM_NORMAL;
/* Register file handler */
- SCH_AddInputFileHandler(fd, read_from_device, NULL);
+ SCH_AddFileHandler(fd, SCH_FILE_INPUT, read_from_device, NULL);
/* Register slew handler */
LCL_AddParameterChangeHandler(slew_samples, NULL);
@@ -585,7 +584,7 @@ RTC_Linux_Finalise(void)
/* Remove input file handler */
if (fd >= 0) {
- SCH_RemoveInputFileHandler(fd);
+ SCH_RemoveFileHandler(fd);
close(fd);
/* Save the RTC data */
@@ -758,7 +757,7 @@ maybe_autotrim(void)
/* ================================================== */
static void
-process_reading(time_t rtc_time, struct timeval *system_time)
+process_reading(time_t rtc_time, struct timespec *system_time)
{
double rtc_fast;
@@ -791,7 +790,7 @@ process_reading(time_t rtc_time, struct timeval *system_time)
if (logfileid != -1) {
- rtc_fast = (double)(rtc_time - system_time->tv_sec) - 1.0e-6 * (double) system_time->tv_usec;
+ rtc_fast = (rtc_time - system_time->tv_sec) - 1.0e-9 * system_time->tv_nsec;
LOG_FileWrite(logfileid, "%s %14.6f %1d %14.6f %12.3f %2d %2d %4d",
UTI_TimeToLogForm(system_time->tv_sec),
@@ -805,11 +804,11 @@ process_reading(time_t rtc_time, struct timeval *system_time)
/* ================================================== */
static void
-read_from_device(void *any)
+read_from_device(int fd_, int event, void *any)
{
int status;
unsigned long data;
- struct timeval sys_time;
+ struct timespec sys_time;
struct rtc_time rtc_raw;
struct tm rtc_tm;
time_t rtc_t;
@@ -821,7 +820,7 @@ read_from_device(void *any)
/* This looks like a bad error : the file descriptor was indicating it was
* ready to read but we couldn't read anything. Give up. */
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
- SCH_RemoveInputFileHandler(fd);
+ SCH_RemoveFileHandler(fd);
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
close(fd);
fd = -1;
@@ -849,7 +848,7 @@ read_from_device(void *any)
goto turn_off_interrupt;
}
- /* Convert RTC time into a struct timeval */
+ /* Convert RTC time into a struct timespec */
rtc_tm.tm_sec = rtc_raw.tm_sec;
rtc_tm.tm_min = rtc_raw.tm_min;
rtc_tm.tm_hour = rtc_raw.tm_hour;
@@ -978,7 +977,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
struct tm rtc_tm;
time_t rtc_t;
double accumulated_error, sys_offset;
- struct timeval new_sys_time, old_sys_time;
+ struct timespec new_sys_time, old_sys_time;
coefs_file_name = CNF_GetRtcFile();
@@ -1032,16 +1031,16 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
new_sys_time.tv_sec = rtc_t;
/* Average error in the RTC reading */
- new_sys_time.tv_usec = 500000;
+ new_sys_time.tv_nsec = 500000000;
- UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
+ UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
return 0;
}
- UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time);
+ sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) {
@@ -1064,7 +1063,7 @@ int
RTC_Linux_GetReport(RPT_RTC_Report *report)
{
report->ref_time.tv_sec = coef_ref_time;
- report->ref_time.tv_usec = 0;
+ report->ref_time.tv_nsec = 0;
report->n_samples = n_samples;
report->n_runs = n_runs;
if (n_samples > 1) {
@@ -1083,8 +1082,7 @@ RTC_Linux_GetReport(RPT_RTC_Report *report)
int
RTC_Linux_Trim(void)
{
- struct timeval now;
-
+ struct timespec now;
/* Remember the slope coefficient - we won't be able to determine a
good one in a few seconds when we determine the new offset! */
@@ -1114,7 +1112,7 @@ RTC_Linux_Trim(void)
/* Estimate the offset in case writertc is called or chronyd
is terminated during rapid sampling */
- coef_seconds_fast = -now.tv_usec / 1e6 + 0.5;
+ coef_seconds_fast = -now.tv_nsec / 1.0e9 + 0.5;
coef_ref_time = now.tv_sec;
/* And start rapid sampling, interrupts on now */
diff --git a/sched.c b/sched.c
index bab926a..0ffbe9e 100644
--- a/sched.c
+++ b/sched.c
@@ -44,17 +44,6 @@ static int initialised = 0;
/* ================================================== */
-/* Variables to handle the capability to dispatch on particular file
- handles becoming readable */
-
-/* Each bit set in this fd set corresponds to a read descriptor that
- we are watching and with which we have a handler associated in the
- file_handlers array */
-static fd_set read_fds;
-
-/* This is the number of bits that we have set in read_fds */
-static unsigned int n_read_fds;
-
/* One more than the highest file descriptor that is registered */
static unsigned int one_highest_fd;
@@ -67,12 +56,13 @@ static unsigned int one_highest_fd;
typedef struct {
SCH_FileHandler handler;
SCH_ArbitraryArgument arg;
+ int events;
} FileHandlerEntry;
static ARR_Instance file_handlers;
/* Timestamp when last select() returned */
-static struct timeval last_select_ts, last_select_ts_raw;
+static struct timespec last_select_ts, last_select_ts_raw;
static double last_select_ts_err;
/* ================================================== */
@@ -83,7 +73,7 @@ typedef struct _TimerQueueEntry
{
struct _TimerQueueEntry *next; /* Forward and back links in the list */
struct _TimerQueueEntry *prev;
- struct timeval tv; /* Local system time at which the
+ struct timespec ts; /* Local system time at which the
timeout is to expire. Clearly this
must be in terms of what the
operating system thinks of as
@@ -111,7 +101,7 @@ static SCH_TimeoutID next_tqe_id;
static TimerQueueEntry *tqe_free_list = NULL;
/* Timestamp when was last timeout dispatched for each class */
-static struct timeval last_class_dispatch[SCH_NumberOfClasses];
+static struct timespec last_class_dispatch[SCH_NumberOfClasses];
/* ================================================== */
@@ -120,8 +110,8 @@ static int need_to_exit;
/* ================================================== */
static void
-handle_slew(struct timeval *raw,
- struct timeval *cooked,
+handle_slew(struct timespec *raw,
+ struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -132,9 +122,6 @@ handle_slew(struct timeval *raw,
void
SCH_Initialise(void)
{
- FD_ZERO(&read_fds);
- n_read_fds = 0;
-
file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry));
n_timer_queue_entries = 0;
@@ -166,73 +153,85 @@ SCH_Finalise(void) {
/* ================================================== */
void
-SCH_AddInputFileHandler
-(int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
+SCH_AddFileHandler
+(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
{
FileHandlerEntry *ptr;
assert(initialised);
+ assert(events);
+ assert(fd >= 0);
if (fd >= FD_SETSIZE)
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
+ /* Resize the array if the descriptor is highest so far */
+ while (ARR_GetSize(file_handlers) <= fd) {
+ ptr = ARR_GetNewElement(file_handlers);
+ ptr->handler = NULL;
+ ptr->arg = NULL;
+ ptr->events = 0;
+ }
+
+ ptr = ARR_GetElement(file_handlers, fd);
+
/* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests
a bug somewhere else in the program. */
- if (FD_ISSET(fd, &read_fds))
- assert(0);
+ assert(!ptr->handler);
- ++n_read_fds;
-
- if (ARR_GetSize(file_handlers) < fd + 1)
- ARR_SetSize(file_handlers, fd + 1);
-
- ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
ptr->handler = handler;
ptr->arg = arg;
+ ptr->events = events;
- FD_SET(fd, &read_fds);
-
- if ((fd + 1) > one_highest_fd) {
+ if (one_highest_fd < fd + 1)
one_highest_fd = fd + 1;
- }
}
/* ================================================== */
void
-SCH_RemoveInputFileHandler(int fd)
+SCH_RemoveFileHandler(int fd)
{
- int fds_left, fd_to_check;
+ FileHandlerEntry *ptr;
assert(initialised);
- /* Check that a handler was registered for the fd in question */
- if (!FD_ISSET(fd, &read_fds))
- assert(0);
+ ptr = ARR_GetElement(file_handlers, fd);
- --n_read_fds;
+ /* Check that a handler was registered for the fd in question */
+ assert(ptr->handler);
- FD_CLR(fd, &read_fds);
+ ptr->handler = NULL;
+ ptr->arg = NULL;
+ ptr->events = 0;
/* Find new highest file descriptor */
- fds_left = n_read_fds;
- fd_to_check = 0;
- while (fds_left > 0) {
- if (FD_ISSET(fd_to_check, &read_fds)) {
- --fds_left;
- }
- ++fd_to_check;
+ while (one_highest_fd > 0) {
+ ptr = ARR_GetElement(file_handlers, one_highest_fd - 1);
+ if (ptr->handler)
+ break;
+ one_highest_fd--;
}
+}
+
+/* ================================================== */
+
+void
+SCH_SetFileHandlerEvents(int fd, int events)
+{
+ FileHandlerEntry *ptr;
- one_highest_fd = fd_to_check;
+ assert(events);
+ ptr = ARR_GetElement(file_handlers, fd);
+ ptr->events = events;
}
/* ================================================== */
void
-SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw)
+SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw)
{
if (cooked) {
*cooked = last_select_ts;
@@ -299,7 +298,7 @@ try_again:
/* ================================================== */
SCH_TimeoutID
-SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
+SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{
TimerQueueEntry *new_tqe;
TimerQueueEntry *ptr;
@@ -311,12 +310,12 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler;
new_tqe->arg = arg;
- new_tqe->tv = *tv;
+ new_tqe->ts = *ts;
new_tqe->class = SCH_ReservedTimeoutValue;
/* Now work out where to insert the new entry in the list */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
- if (UTI_CompareTimevals(&new_tqe->tv, &ptr->tv) == -1) {
+ if (UTI_CompareTimespecs(&new_tqe->ts, &ptr->ts) == -1) {
/* If the new entry comes before the current pointer location in
the list, we want to insert the new entry just before ptr. */
break;
@@ -343,14 +342,14 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
SCH_TimeoutID
SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{
- struct timeval now, then;
+ struct timespec now, then;
assert(initialised);
assert(delay >= 0.0);
LCL_ReadRawTime(&now);
- UTI_AddDoubleToTimeval(&now, delay, &then);
- if (UTI_CompareTimevals(&now, &then) > 0) {
+ UTI_AddDoubleToTimespec(&now, delay, &then);
+ if (UTI_CompareTimespecs(&now, &then) > 0) {
LOG_FATAL(LOGF_Scheduler, "Timeout overflow");
}
@@ -367,7 +366,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
{
TimerQueueEntry *new_tqe;
TimerQueueEntry *ptr;
- struct timeval now;
+ struct timespec now;
double diff, r;
double new_min_delay;
@@ -376,10 +375,10 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
assert(class < SCH_NumberOfClasses);
if (randomness > 0.0) {
- uint16_t rnd;
+ uint32_t rnd;
UTI_GetRandomBytes(&rnd, sizeof (rnd));
- r = rnd / (double)0xffff * randomness + 1.0;
+ r = rnd * (randomness / (uint32_t)-1) + 1.0;
min_delay *= r;
separation *= r;
}
@@ -388,7 +387,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
new_min_delay = min_delay;
/* Check the separation from the last dispatched timeout */
- UTI_DiffTimevalsToDouble(&diff, &now, &last_class_dispatch[class]);
+ diff = UTI_DiffTimespecsToDouble(&now, &last_class_dispatch[class]);
if (diff < separation && diff >= 0.0 && diff + new_min_delay < separation) {
new_min_delay = separation - diff;
}
@@ -397,7 +396,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
if necessary to keep at least the separation away */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
if (ptr->class == class) {
- UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now);
+ diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
if (new_min_delay > diff) {
if (new_min_delay - diff < separation) {
new_min_delay = diff + separation;
@@ -411,7 +410,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
}
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
- UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now);
+ diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
if (diff > new_min_delay) {
break;
}
@@ -423,7 +422,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler;
new_tqe->arg = arg;
- UTI_AddDoubleToTimeval(&now, new_min_delay, &new_tqe->tv);
+ UTI_AddDoubleToTimespec(&now, new_min_delay, &new_tqe->ts);
new_tqe->class = class;
new_tqe->next = ptr;
@@ -477,7 +476,7 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
completed). */
static void
-dispatch_timeouts(struct timeval *now) {
+dispatch_timeouts(struct timespec *now) {
TimerQueueEntry *ptr;
SCH_TimeoutHandler handler;
SCH_ArbitraryArgument arg;
@@ -487,7 +486,7 @@ dispatch_timeouts(struct timeval *now) {
LCL_ReadRawTime(now);
if (!(n_timer_queue_entries > 0 &&
- UTI_CompareTimevals(now, &(timer_queue.next->tv)) >= 0)) {
+ UTI_CompareTimespecs(now, &timer_queue.next->ts) >= 0)) {
break;
}
@@ -520,35 +519,49 @@ dispatch_timeouts(struct timeval *now) {
/* ================================================== */
-/* nfh is the number of bits set in fhs */
+/* nfd is the number of bits set in all fd_sets */
static void
-dispatch_filehandlers(int nfh, fd_set *fhs)
+dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
{
FileHandlerEntry *ptr;
- int fh = 0;
+ int fd;
- while (nfh > 0) {
- if (FD_ISSET(fh, fhs)) {
+ for (fd = 0; nfd && fd < one_highest_fd; fd++) {
+ if (except_fds && FD_ISSET(fd, except_fds)) {
+ /* This descriptor has an exception, dispatch its handler */
+ ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
+ (ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
+ nfd--;
+
+ /* Don't try to read from it now */
+ if (read_fds && FD_ISSET(fd, read_fds)) {
+ FD_CLR(fd, read_fds);
+ nfd--;
+ }
+ }
+ if (read_fds && FD_ISSET(fd, read_fds)) {
/* This descriptor can be read from, dispatch its handler */
- ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fh);
- (ptr->handler)(ptr->arg);
-
- /* Decrement number of readable files still to find */
- --nfh;
+ ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
+ (ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
+ nfd--;
}
- ++fh;
+ if (write_fds && FD_ISSET(fd, write_fds)) {
+ /* This descriptor can be written to, dispatch its handler */
+ ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
+ (ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
+ nfd--;
+ }
}
-
}
/* ================================================== */
static void
-handle_slew(struct timeval *raw,
- struct timeval *cooked,
+handle_slew(struct timespec *raw,
+ struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -566,17 +579,69 @@ handle_slew(struct timeval *raw,
/* If a step change occurs, just shift all raw time stamps by the offset */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
- UTI_AddDoubleToTimeval(&ptr->tv, -doffset, &ptr->tv);
+ UTI_AddDoubleToTimespec(&ptr->ts, -doffset, &ptr->ts);
}
for (i = 0; i < SCH_NumberOfClasses; i++) {
- UTI_AddDoubleToTimeval(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]);
+ UTI_AddDoubleToTimespec(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]);
}
- UTI_AddDoubleToTimeval(&last_select_ts_raw, -doffset, &last_select_ts_raw);
+ UTI_AddDoubleToTimespec(&last_select_ts_raw, -doffset, &last_select_ts_raw);
}
- UTI_AdjustTimeval(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset);
+ UTI_AdjustTimespec(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset);
+}
+
+/* ================================================== */
+
+static void
+fill_fd_sets(fd_set **read_fds, fd_set **write_fds, fd_set **except_fds)
+{
+ FileHandlerEntry *handlers;
+ fd_set *rd, *wr, *ex;
+ int i, n, events;
+
+ n = ARR_GetSize(file_handlers);
+ handlers = ARR_GetElements(file_handlers);
+ rd = wr = ex = NULL;
+
+ for (i = 0; i < n; i++) {
+ events = handlers[i].events;
+
+ if (!events)
+ continue;
+
+ if (events & SCH_FILE_INPUT) {
+ if (!rd) {
+ rd = *read_fds;
+ FD_ZERO(rd);
+ }
+ FD_SET(i, rd);
+ }
+
+ if (events & SCH_FILE_OUTPUT) {
+ if (!wr) {
+ wr = *write_fds;
+ FD_ZERO(wr);
+ }
+ FD_SET(i, wr);
+ }
+
+ if (events & SCH_FILE_EXCEPTION) {
+ if (!ex) {
+ ex = *except_fds;
+ FD_ZERO(ex);
+ }
+ FD_SET(i, ex);
+ }
+ }
+
+ if (!rd)
+ *read_fds = NULL;
+ if (!wr)
+ *write_fds = NULL;
+ if (!ex)
+ *except_fds = NULL;
}
/* ================================================== */
@@ -584,31 +649,33 @@ handle_slew(struct timeval *raw,
#define JUMP_DETECT_THRESHOLD 10
static int
-check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
+check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
struct timeval *orig_select_tv,
struct timeval *rem_select_tv)
{
- struct timeval elapsed_min, elapsed_max;
+ struct timespec elapsed_min, elapsed_max, orig_select_ts, rem_select_ts;
double step, elapsed;
+ UTI_TimevalToTimespec(orig_select_tv, &orig_select_ts);
+
/* Get an estimate of the time spent waiting in the select() call. On some
systems (e.g. Linux) the timeout timeval is modified to return the
remaining time, use that information. */
if (timeout) {
- elapsed_max = elapsed_min = *orig_select_tv;
+ elapsed_max = elapsed_min = orig_select_ts;
} else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
(rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
- UTI_DiffTimevals(&elapsed_min, orig_select_tv, rem_select_tv);
+ UTI_TimevalToTimespec(rem_select_tv, &rem_select_ts);
+ UTI_DiffTimespecs(&elapsed_min, &orig_select_ts, &rem_select_ts);
elapsed_max = elapsed_min;
} else {
if (rem_select_tv)
- elapsed_max = *orig_select_tv;
+ elapsed_max = orig_select_ts;
else
- UTI_DiffTimevals(&elapsed_max, raw, prev_raw);
- elapsed_min.tv_sec = 0;
- elapsed_min.tv_usec = 0;
+ UTI_DiffTimespecs(&elapsed_max, raw, prev_raw);
+ UTI_ZeroTimespec(&elapsed_min);
}
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
@@ -621,8 +688,8 @@ check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
return 1;
}
- UTI_DiffTimevalsToDouble(&step, &last_select_ts_raw, raw);
- UTI_TimevalToDouble(&elapsed_min, &elapsed);
+ step = UTI_DiffTimespecsToDouble(&last_select_ts_raw, raw);
+ elapsed = UTI_TimespecToDouble(&elapsed_min);
step += elapsed;
/* Cooked time may no longer be valid after dispatching the handlers */
@@ -636,10 +703,11 @@ check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
void
SCH_MainLoop(void)
{
- fd_set rd;
+ fd_set read_fds, write_fds, except_fds;
+ fd_set *p_read_fds, *p_write_fds, *p_except_fds;
int status, errsv;
struct timeval tv, saved_tv, *ptv;
- struct timeval now, saved_now, cooked;
+ struct timespec ts, now, saved_now, cooked;
double err;
assert(initialised);
@@ -655,28 +723,28 @@ SCH_MainLoop(void)
/* Check whether there is a timeout and set it up */
if (n_timer_queue_entries > 0) {
+ UTI_DiffTimespecs(&ts, &timer_queue.next->ts, &now);
+ assert(ts.tv_sec > 0 || ts.tv_nsec > 0);
- UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now);
+ UTI_TimespecToTimeval(&ts, &tv);
ptv = &tv;
- assert(tv.tv_sec > 0 || tv.tv_usec > 0);
saved_tv = tv;
-
} else {
ptv = NULL;
- /* This is needed to fix a compiler warning */
- saved_tv.tv_sec = 0;
+ saved_tv.tv_sec = saved_tv.tv_usec = 0;
}
+ p_read_fds = &read_fds;
+ p_write_fds = &write_fds;
+ p_except_fds = &except_fds;
+ fill_fd_sets(&p_read_fds, &p_write_fds, &p_except_fds);
+
/* if there are no file descriptors being waited on and no
timeout set, this is clearly ridiculous, so stop the run */
- if (!ptv && !n_read_fds) {
+ if (!ptv && !p_read_fds && !p_write_fds)
LOG_FATAL(LOGF_Scheduler, "Nothing to do");
- }
- /* Copy current set of read file descriptors */
- memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
-
- status = select(one_highest_fd, &rd, NULL, NULL, ptv);
+ status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
errsv = errno;
LCL_ReadRawTime(&now);
@@ -697,10 +765,8 @@ SCH_MainLoop(void)
LOG_FATAL(LOGF_Scheduler, "select() failed : %s", strerror(errsv));
}
} else if (status > 0) {
- /* A file descriptor is ready to read */
-
- dispatch_filehandlers(status, &rd);
-
+ /* A file descriptor is ready for input or output */
+ dispatch_filehandlers(status, p_read_fds, p_write_fds, p_except_fds);
} else {
/* No descriptors readable, timeout must have elapsed.
Therefore, tv must be non-null */
diff --git a/sched.h b/sched.h
index 6d297d3..837c914 100644
--- a/sched.h
+++ b/sched.h
@@ -40,7 +40,7 @@ typedef enum {
} SCH_TimeoutClass;
typedef void* SCH_ArbitraryArgument;
-typedef void (*SCH_FileHandler)(SCH_ArbitraryArgument);
+typedef void (*SCH_FileHandler)(int fd, int event, SCH_ArbitraryArgument);
typedef void (*SCH_TimeoutHandler)(SCH_ArbitraryArgument);
/* Exported functions */
@@ -51,19 +51,21 @@ extern void SCH_Initialise(void);
/* Finalisation function for the module */
extern void SCH_Finalise(void);
+/* File events */
+#define SCH_FILE_INPUT 1
+#define SCH_FILE_OUTPUT 2
+#define SCH_FILE_EXCEPTION 4
+
/* Register a handler for when select goes true on a file descriptor */
-extern void SCH_AddInputFileHandler
-(int fd, /* The file descriptor */
- SCH_FileHandler, /* The handler routine */
- SCH_ArbitraryArgument /* An arbitrary passthrough argument to the handler */
-);
-extern void SCH_RemoveInputFileHandler(int fd);
+extern void SCH_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
+extern void SCH_RemoveFileHandler(int fd);
+extern void SCH_SetFileHandlerEvents(int fd, int events);
/* Get the time stamp taken after a file descriptor became ready or a timeout expired */
-extern void SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw);
+extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);
/* This queues a timeout to elapse at a given (raw) local time */
-extern SCH_TimeoutID SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler, SCH_ArbitraryArgument);
+extern SCH_TimeoutID SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg);
/* This queues a timeout to elapse at a given delta time relative to the current (raw) time */
extern SCH_TimeoutID SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler, SCH_ArbitraryArgument);
diff --git a/smooth.c b/smooth.c
index a558d55..08b80ba 100644
--- a/smooth.c
+++ b/smooth.c
@@ -93,17 +93,17 @@ static double max_freq;
/* Frequency offset, time offset and the time of the last smoothing update */
static double smooth_freq;
static double smooth_offset;
-static struct timeval last_update;
+static struct timespec last_update;
static void
-get_smoothing(struct timeval *now, double *poffset, double *pfreq,
+get_smoothing(struct timespec *now, double *poffset, double *pfreq,
double *pwander)
{
double elapsed, length, offset, freq, wander;
int i;
- UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
+ elapsed = UTI_DiffTimespecsToDouble(now, &last_update);
offset = smooth_offset;
freq = smooth_freq;
@@ -214,7 +214,7 @@ update_stages(void)
}
static void
-update_smoothing(struct timeval *now, double offset, double freq)
+update_smoothing(struct timespec *now, double offset, double freq)
{
/* Don't accept offset/frequency until the clock has stabilized */
if (locked) {
@@ -234,7 +234,7 @@ update_smoothing(struct timeval *now, double offset, double freq)
}
static void
-handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
+handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
double delta;
@@ -246,7 +246,7 @@ handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
update_smoothing(cooked, doffset, dfreq);
}
- UTI_AdjustTimeval(&last_update, cooked, &last_update, &delta, dfreq, doffset);
+ UTI_AdjustTimespec(&last_update, cooked, &last_update, &delta, dfreq, doffset);
}
void SMT_Initialise(void)
@@ -277,7 +277,7 @@ int SMT_IsEnabled(void)
}
double
-SMT_GetOffset(struct timeval *now)
+SMT_GetOffset(struct timespec *now)
{
double offset, freq;
@@ -290,7 +290,7 @@ SMT_GetOffset(struct timeval *now)
}
void
-SMT_Activate(struct timeval *now)
+SMT_Activate(struct timespec *now)
{
if (!enabled || !locked)
return;
@@ -302,7 +302,7 @@ SMT_Activate(struct timeval *now)
}
void
-SMT_Reset(struct timeval *now)
+SMT_Reset(struct timespec *now)
{
int i;
@@ -318,7 +318,7 @@ SMT_Reset(struct timeval *now)
}
void
-SMT_Leap(struct timeval *now, int leap)
+SMT_Leap(struct timespec *now, int leap)
{
/* When the leap-only mode is disabled, the leap second will be accumulated
in handle_slew() as a normal offset */
@@ -329,7 +329,7 @@ SMT_Leap(struct timeval *now, int leap)
}
int
-SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now)
+SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timespec *now)
{
double length, elapsed;
int i;
@@ -346,7 +346,7 @@ SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now)
report->freq_ppm *= -1.0e6;
report->wander_ppm *= -1.0e6;
- UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
+ elapsed = UTI_DiffTimespecsToDouble(now, &last_update);
if (!locked && elapsed >= 0.0) {
for (i = 0, length = 0.0; i < NUM_STAGES; i++)
length += stages[i].length;
diff --git a/smooth.h b/smooth.h
index 72e67af..4e84504 100644
--- a/smooth.h
+++ b/smooth.h
@@ -35,14 +35,14 @@ extern void SMT_Finalise(void);
extern int SMT_IsEnabled(void);
-extern double SMT_GetOffset(struct timeval *now);
+extern double SMT_GetOffset(struct timespec *now);
-extern void SMT_Activate(struct timeval *now);
+extern void SMT_Activate(struct timespec *now);
-extern void SMT_Reset(struct timeval *now);
+extern void SMT_Reset(struct timespec *now);
-extern void SMT_Leap(struct timeval *now, int leap);
+extern void SMT_Leap(struct timespec *now, int leap);
-extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now);
+extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timespec *now);
#endif
diff --git a/sources.c b/sources.c
index 550d109..5c9bbe3 100644
--- a/sources.c
+++ b/sources.c
@@ -56,7 +56,7 @@ static int initialised = 0;
struct SelectInfo {
int stratum;
int select_ok;
- double variance;
+ double std_dev;
double root_distance;
double lo_limit;
double hi_limit;
@@ -71,12 +71,12 @@ typedef enum {
SRC_UNSELECTABLE, /* Has noselect option set */
SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
+ SRC_JITTERY, /* Had std dev larger than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
SRC_STALE, /* Has older samples than others */
SRC_ORPHAN, /* Has stratum equal or larger than orphan stratum */
SRC_UNTRUSTED, /* Overlaps trusted sources */
SRC_FALSETICKER, /* Doesn't agree with others */
- SRC_JITTERY, /* Scatter worse than other's dispersion (not used) */
SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */
SRC_NONPREFERRED, /* Others have prefer option */
SRC_WAITS_UPDATE, /* No updates, selection postponed */
@@ -159,6 +159,7 @@ static int selected_source_index; /* Which source index is currently
#define DISTANT_PENALTY 32
static double max_distance;
+static double max_jitter;
static double reselect_distance;
static double stratum_weight;
static double combine_limit;
@@ -167,7 +168,7 @@ static double combine_limit;
/* Forward prototype */
static void
-slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq,
+slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
static void
add_dispersion(double dispersion, void *anything);
@@ -184,6 +185,7 @@ void SRC_Initialise(void) {
max_n_sources = 0;
selected_source_index = INVALID_SOURCE;
max_distance = CNF_GetMaxDistance();
+ max_jitter = CNF_GetMaxJitter();
reselect_distance = CNF_GetReselectDistance();
stratum_weight = CNF_GetStratumWeight();
combine_limit = CNF_GetCombineLimit();
@@ -309,22 +311,12 @@ SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr)
}
/* ================================================== */
-/* Function to get the range of frequencies, relative to the given
- source, that we believe the local clock lies within. The return
- values are in terms of the number of seconds fast (+ve) or slow
- (-ve) relative to the source that the local clock becomes after a
- given amount of local time has elapsed.
-
- Suppose the initial offset relative to the source is U (fast +ve,
- slow -ve) and a time interval T elapses measured in terms of the
- local clock. Then the error relative to the source at the end of
- the interval should lie in the interval [U+T*lo, U+T*hi]. */
-
-void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
+
+SST_Stats
+SRC_GetSourcestats(SRC_Instance instance)
{
assert(initialised);
-
- SST_GetFrequencyRange(instance->stats, lo, hi);
+ return instance->stats;
}
/* ================================================== */
@@ -342,7 +334,7 @@ void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
void SRC_AccumulateSample
(SRC_Instance inst,
- struct timeval *sample_time,
+ struct timespec *sample_time,
double offset,
double peer_delay,
double peer_dispersion,
@@ -357,7 +349,8 @@ void SRC_AccumulateSample
inst->leap_status = leap_status;
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
- source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
+ source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
+ root_delay, root_dispersion, stratum);
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
@@ -404,7 +397,7 @@ special_mode_end(void)
/* Check if the source could still have enough samples to be selectable */
if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size +
- SRC_Samples(sources[i]) >= MIN_SAMPLES_FOR_REGRESS)
+ SST_Samples(sources[i]->stats) >= MIN_SAMPLES_FOR_REGRESS)
return 0;
}
@@ -514,10 +507,10 @@ mark_ok_sources(SRC_Status status)
/* ================================================== */
static int
-combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
+combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
double *offset_sd, double *frequency, double *skew)
{
- struct timeval src_ref_time;
+ struct timespec src_ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
@@ -564,7 +557,7 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
if (sources[index]->status == SRC_OK)
sources[index]->status = SRC_UNSELECTED;
- UTI_DiffTimevalsToDouble(&elapsed, ref_time, &src_ref_time);
+ elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency;
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / src_skew;
@@ -604,12 +597,12 @@ void
SRC_SelectSource(SRC_Instance updated_inst)
{
struct SelectInfo *si;
- struct timeval now, ref_time;
+ struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources;
int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
int depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index;
- int orphan_stratum, orphan_source;
+ int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score;
@@ -657,7 +650,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
&si->lo_limit, &si->hi_limit, &si->root_distance,
- &si->variance, &first_sample_ago,
+ &si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok);
if (!si->select_ok) {
@@ -674,6 +667,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
+ /* And the same applies for the estimated standard deviation */
+ if (si->std_dev > max_jitter) {
+ sources[i]->status = SRC_JITTERY;
+ continue;
+ }
+
sources[i]->status = SRC_OK; /* For now */
if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago)
@@ -910,18 +909,22 @@ SRC_SelectSource(SRC_Instance updated_inst)
return;
}
- /* Accept leap second status if more than half of selectable sources agree */
- for (i = j1 = j2 = 0; i < n_sel_sources; i++) {
+ /* Accept leap second status if more than half of selectable (and trusted
+ if there are any) sources agree */
+ for (i = leap_ins = leap_del = leap_votes = 0; i < n_sel_sources; i++) {
index = sel_sources[i];
+ if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
+ continue;
+ leap_votes++;
if (sources[index]->leap_status == LEAP_InsertSecond)
- j1++;
+ leap_ins++;
else if (sources[index]->leap_status == LEAP_DeleteSecond)
- j2++;
+ leap_del++;
}
- if (j1 > n_sel_sources / 2)
+ if (leap_ins > leap_votes / 2)
leap_status = LEAP_InsertSecond;
- else if (j2 > n_sel_sources / 2)
+ else if (leap_del > leap_votes / 2)
leap_status = LEAP_DeleteSecond;
else
leap_status = LEAP_Normal;
@@ -1099,32 +1102,6 @@ SRC_SetReselectDistance(double distance)
}
/* ================================================== */
-
-double
-SRC_PredictOffset(SRC_Instance inst, struct timeval *when)
-{
- return SST_PredictOffset(inst->stats, when);
-}
-
-/* ================================================== */
-
-double
-SRC_MinRoundTripDelay(SRC_Instance inst)
-{
- return SST_MinRoundTripDelay(inst->stats);
-}
-
-/* ================================================== */
-
-int
-SRC_IsGoodSample(SRC_Instance inst, double offset, double delay,
- double max_delay_dev_ratio, double clock_error, struct timeval *when)
-{
- return SST_IsGoodSample(inst->stats, offset, delay, max_delay_dev_ratio,
- clock_error, when);
-}
-
-/* ================================================== */
/* This routine is registered as a callback with the local clock
module, to be called whenever the local clock changes frequency or
is slewed. It runs through all the existing source statistics, and
@@ -1132,12 +1109,8 @@ SRC_IsGoodSample(SRC_Instance inst, double offset, double delay,
the new regime. */
static void
-slew_sources(struct timeval *raw,
- struct timeval *cooked,
- double dfreq,
- double doffset,
- LCL_ChangeType change_type,
- void *anything)
+slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
+ double doffset, LCL_ChangeType change_type, void *anything)
{
int i;
@@ -1170,40 +1143,54 @@ add_dispersion(double dispersion, void *anything)
}
/* ================================================== */
+
+static
+FILE *open_dumpfile(SRC_Instance inst, const char *mode)
+{
+ FILE *f;
+ char filename[1024], *dumpdir;
+
+ dumpdir = CNF_GetDumpDir();
+ if (dumpdir[0] == '\0') {
+ LOG(LOGS_WARN, LOGF_Sources, "dumpdir not specified");
+ return NULL;
+ }
+
+ /* Include IP address in the name for NTP sources, or reference ID in hex */
+ if ((inst->type == SRC_NTP &&
+ snprintf(filename, sizeof (filename), "%s/%s.dat", dumpdir,
+ source_to_string(inst)) >= sizeof (filename)) ||
+ (inst->type != SRC_NTP &&
+ snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat",
+ dumpdir, inst->ref_id) >= sizeof (filename))) {
+ LOG(LOGS_WARN, LOGF_Sources, "dumpdir too long");
+ return NULL;
+ }
+
+ f = fopen(filename, mode);
+ if (!f && mode[0] != 'r')
+ LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file for %s",
+ source_to_string(inst));
+
+ return f;
+}
+
+/* ================================================== */
/* This is called to dump out the source measurement registers */
void
SRC_DumpSources(void)
{
FILE *out;
- int direc_len, file_len;
- char *filename;
- unsigned int a, b, c, d;
int i;
- char *direc;
-
- direc = CNF_GetDumpDir();
- direc_len = strlen(direc);
- file_len = direc_len + 24;
- filename = MallocArray(char, file_len); /* a bit of slack */
for (i = 0; i < n_sources; i++) {
- a = (sources[i]->ref_id) >> 24;
- b = ((sources[i]->ref_id) >> 16) & 0xff;
- c = ((sources[i]->ref_id) >> 8) & 0xff;
- d = ((sources[i]->ref_id)) & 0xff;
-
- snprintf(filename, file_len - 1, "%s/%d.%d.%d.%d.dat", direc, a, b, c, d);
- out = fopen(filename, "w");
- if (!out) {
- LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
- } else {
- SST_SaveToFile(sources[i]->stats, out);
- fclose(out);
- }
+ out = open_dumpfile(sources[i], "w");
+ if (!out)
+ continue;
+ SST_SaveToFile(sources[i]->stats, out);
+ fclose(out);
}
-
- Free(filename);
}
/* ================================================== */
@@ -1212,36 +1199,59 @@ void
SRC_ReloadSources(void)
{
FILE *in;
- char *filename;
- unsigned int a, b, c, d;
int i;
- char *dumpdir;
- int dumpdirlen, filelen;
- for (i=0; i<n_sources; i++) {
- a = (sources[i]->ref_id) >> 24;
- b = ((sources[i]->ref_id) >> 16) & 0xff;
- c = ((sources[i]->ref_id) >> 8) & 0xff;
- d = ((sources[i]->ref_id)) & 0xff;
-
- dumpdir = CNF_GetDumpDir();
- dumpdirlen = strlen(dumpdir);
- filelen = dumpdirlen + 24;
- filename = MallocArray(char, filelen);
- snprintf(filename, filelen-1, "%s/%d.%d.%d.%d.dat", dumpdir, a, b, c, d);
- in = fopen(filename, "r");
- if (!in) {
- LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
- } else {
- if (SST_LoadFromFile(sources[i]->stats, in)) {
- SST_DoNewRegression(sources[i]->stats);
- } else {
- LOG(LOGS_WARN, LOGF_Sources, "Problem loading from file %s", filename);
- }
- fclose(in);
- }
- Free(filename);
+ for (i = 0; i < n_sources; i++) {
+ in = open_dumpfile(sources[i], "r");
+ if (!in)
+ continue;
+ if (!SST_LoadFromFile(sources[i]->stats, in))
+ LOG(LOGS_WARN, LOGF_Sources, "Could not load dump file for %s",
+ source_to_string(sources[i]));
+ else
+ LOG(LOGS_INFO, LOGF_Sources, "Loaded dump file for %s",
+ source_to_string(sources[i]));
+ fclose(in);
+ }
+}
+
+/* ================================================== */
+
+void
+SRC_RemoveDumpFiles(void)
+{
+ char pattern[1024], name[64], *dumpdir, *s;
+ IPAddr ip_addr;
+ glob_t gl;
+ size_t i;
+
+ dumpdir = CNF_GetDumpDir();
+ if (dumpdir[0] == '\0' ||
+ snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern))
+ return;
+
+ if (glob(pattern, 0, NULL, &gl))
+ return;
+
+ for (i = 0; i < gl.gl_pathc; i++) {
+ s = strrchr(gl.gl_pathv[i], '/');
+ if (!s || snprintf(name, sizeof (name), "%s", s + 1) >= sizeof (name))
+ continue;
+
+ /* Remove .dat extension */
+ if (strlen(name) < 4)
+ continue;
+ name[strlen(name) - 4] = '\0';
+
+ /* Check if it looks like name of an actual dump file */
+ if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
+ continue;
+
+ DEBUG_LOG(LOGF_Sources, "Removing %s", gl.gl_pathv[i]);
+ unlink(gl.gl_pathv[i]);
}
+
+ globfree(&gl);
}
/* ================================================== */
@@ -1290,7 +1300,7 @@ SRC_ActiveSources(void)
/* ================================================== */
int
-SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
+SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
{
SRC_Instance src;
if ((index >= n_sources) || (index < 0)) {
@@ -1298,7 +1308,6 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
} else {
src = sources[index];
- memset(&report->ip_addr, 0, sizeof (report->ip_addr));
if (src->ip_addr)
report->ip_addr = *src->ip_addr;
else {
@@ -1308,14 +1317,6 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
}
switch (src->status) {
- case SRC_UNSELECTABLE:
- case SRC_BAD_STATS:
- case SRC_BAD_DISTANCE:
- case SRC_STALE:
- case SRC_ORPHAN:
- case SRC_WAITS_STATS:
- report->state = RPT_UNREACH;
- break;
case SRC_FALSETICKER:
report->state = RPT_FALSETICKER;
break;
@@ -1336,9 +1337,8 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
case SRC_SELECTED:
report->state = RPT_SYNC;
break;
- case SRC_OK:
default:
- assert(0);
+ report->state = RPT_UNREACH;
break;
}
@@ -1356,7 +1356,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
/* ================================================== */
int
-SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now)
+SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now)
{
SRC_Instance src;
@@ -1385,11 +1385,3 @@ SRC_GetType(int index)
}
/* ================================================== */
-
-int
-SRC_Samples(SRC_Instance inst)
-{
- return SST_Samples(inst->stats);
-}
-
-/* ================================================== */
diff --git a/sources.h b/sources.h
index 4187d13..9acf98b 100644
--- a/sources.h
+++ b/sources.h
@@ -34,6 +34,7 @@
#include "ntp.h"
#include "reports.h"
+#include "sourcestats.h"
/* Size of the source reachability register */
#define SOURCE_REACH_BITS 8
@@ -73,18 +74,8 @@ extern void SRC_ResetInstance(SRC_Instance instance);
/* Function to change the sources's reference ID and IP address */
extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
-/* Function to get the range of frequencies, relative to the given
- source, that we believe the local clock lies within. The return
- values are in terms of the number of seconds fast (+ve) or slow
- (-ve) relative to the source that the local clock becomes after a
- given amount of local time has elapsed.
-
- Suppose the initial offset relative to the source is U (fast +ve,
- slow -ve) and a time interval T elapses measured in terms of the
- local clock. Then the error relative to the source at the end of
- the interval should lie in the interval [U+T*lo, U+T*hi]. */
-
-extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi);
+/* Function to get access to the sourcestats instance */
+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.
@@ -114,7 +105,7 @@ extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
*/
-extern void SRC_AccumulateSample(SRC_Instance instance, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
+extern void SRC_AccumulateSample(SRC_Instance instance, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
/* This routine sets the source as receiving reachability updates */
extern void SRC_SetActive(SRC_Instance inst);
@@ -143,34 +134,18 @@ extern void SRC_ReselectSource(void);
/* Set reselect distance */
extern void SRC_SetReselectDistance(double distance);
-/* Predict the offset of the local clock relative to a given source at
- a given local cooked time. Positive indicates local clock is FAST
- relative to reference. */
-extern double SRC_PredictOffset(SRC_Instance inst, struct timeval *when);
-
-/* Return the minimum peer delay amongst the previous samples
- currently held in the register */
-extern double SRC_MinRoundTripDelay(SRC_Instance inst);
-
-/* This routine determines if a new sample is good enough that it should be
- accumulated */
-extern int SRC_IsGoodSample(SRC_Instance inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when);
-
extern void SRC_DumpSources(void);
-
extern void SRC_ReloadSources(void);
+extern void SRC_RemoveDumpFiles(void);
extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void);
-extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now);
+extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
-extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now);
+extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
extern SRC_Type SRC_GetType(int index);
-extern int SRC_Samples(SRC_Instance inst);
-
#endif /* GOT_SOURCES_H */
-
diff --git a/sourcestats.c b/sourcestats.c
index ac976dc..eecd232 100644
--- a/sourcestats.c
+++ b/sourcestats.c
@@ -50,6 +50,19 @@
/* The minimum allowed skew */
#define MIN_SKEW 1.0e-12
+/* The asymmetry of network jitter when all jitter is in one direction */
+#define MAX_ASYMMETRY 0.5
+
+/* The minimum estimated asymmetry that can activate the offset correction */
+#define MIN_ASYMMETRY 0.45
+
+/* The minimum number of consecutive asymmetries with the same sign needed
+ to activate the offset correction */
+#define MIN_ASYMMETRY_RUN 10
+
+/* The maximum value of the counter */
+#define MAX_ASYMMETRY_RUN 1000
+
/* ================================================== */
static LOG_FileID logfileid;
@@ -72,8 +85,8 @@ struct SST_Stats_Record {
buffer. */
int n_samples;
- /* Number of extra samples stored in sample_times and offsets arrays that are
- used to extend runs test */
+ /* Number of extra samples stored in sample_times, offsets and peer_delays
+ arrays that are used to extend the runs test */
int runs_samples;
/* The index of the newest sample */
@@ -92,11 +105,18 @@ struct SST_Stats_Record {
/* This is the estimated offset (+ve => local fast) at a particular time */
double estimated_offset;
double estimated_offset_sd;
- struct timeval offset_time;
+ struct timespec offset_time;
/* Number of runs of the same sign amongst the residuals */
int nruns;
+ /* Number of consecutive estimated asymmetries with the same sign.
+ The sign of the number encodes the sign of the asymmetry. */
+ int asymmetry_run;
+
+ /* This is the latest estimated asymmetry of network jitter */
+ double asymmetry;
+
/* This value contains the estimated frequency. This is the number
of seconds that the local clock gains relative to the reference
source per unit local time. (Positive => local clock fast,
@@ -108,12 +128,12 @@ struct SST_Stats_Record {
about estimated_frequency */
double skew;
- /* This is the estimated residual variance of the data points */
- double variance;
+ /* This is the estimated standard deviation of the data points */
+ double std_dev;
/* This array contains the sample epochs, in terms of the local
clock. */
- struct timeval sample_times[MAX_SAMPLES * REGRESS_RUNS_RATIO];
+ struct timespec sample_times[MAX_SAMPLES * REGRESS_RUNS_RATIO];
/* This is an array of offsets, in seconds, corresponding to the
sample times. In this module, we use the convention that
@@ -131,7 +151,7 @@ struct SST_Stats_Record {
/* This is an array of peer delays, in seconds, being the roundtrip
measurement delay to the peer */
- double peer_delays[MAX_SAMPLES];
+ double peer_delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
/* This is an array of peer dispersions, being the skew and local
precision dispersion terms from sampling the peer */
@@ -161,7 +181,7 @@ void
SST_Initialise(void)
{
logfileid = CNF_GetLogStatistics() ? LOG_FileOpen("statistics",
- " Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr")
+ " Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr Asym")
: -1;
}
@@ -214,10 +234,11 @@ SST_ResetInstance(SST_Stats inst)
inst->skew = 2000.0e-6;
inst->estimated_offset = 0.0;
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
- inst->offset_time.tv_sec = 0;
- inst->offset_time.tv_usec = 0;
- inst->variance = 16.0;
+ UTI_ZeroTimespec(&inst->offset_time);
+ inst->std_dev = 4.0;
inst->nruns = 0;
+ inst->asymmetry_run = 0;
+ inst->asymmetry = 0.0;
}
/* ================================================== */
@@ -253,7 +274,7 @@ prune_register(SST_Stats inst, int new_oldest)
/* ================================================== */
void
-SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
+SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
double offset,
double peer_delay, double peer_dispersion,
double root_delay, double root_dispersion,
@@ -269,7 +290,7 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
/* Make sure it's newer than the last sample */
if (inst->n_samples &&
- UTI_CompareTimevals(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
+ UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
SST_ResetInstance(inst);
@@ -282,14 +303,14 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
inst->sample_times[n] = *sample_time;
inst->offsets[n] = offset;
inst->orig_offsets[m] = offset;
- inst->peer_delays[m] = peer_delay;
+ inst->peer_delays[n] = peer_delay;
inst->peer_dispersions[m] = peer_dispersion;
inst->root_delays[m] = root_delay;
inst->root_dispersions[m] = root_dispersion;
inst->strata[m] = stratum;
- if (!inst->n_samples || inst->peer_delays[m] < inst->peer_delays[inst->min_delay_sample])
- inst->min_delay_sample = m;
+ if (!inst->n_samples || inst->peer_delays[n] < inst->peer_delays[inst->min_delay_sample])
+ inst->min_delay_sample = n;
++inst->n_samples;
}
@@ -323,14 +344,13 @@ get_buf_index(SST_Stats inst, int i)
static void
convert_to_intervals(SST_Stats inst, double *times_back)
{
- struct timeval *newest_tv;
+ struct timespec *ts;
int i;
- newest_tv = &(inst->sample_times[inst->last_sample]);
+ ts = &inst->sample_times[inst->last_sample];
for (i = -inst->runs_samples; i < inst->n_samples; i++) {
/* The entries in times_back[] should end up negative */
- UTI_DiffTimevalsToDouble(&times_back[i],
- &inst->sample_times[get_runsbuf_index(inst, i)], newest_tv);
+ times_back[i] = UTI_DiffTimespecsToDouble(&inst->sample_times[get_runsbuf_index(inst, i)], ts);
}
}
@@ -376,16 +396,66 @@ find_min_delay_sample(SST_Stats inst)
{
int i, index;
- inst->min_delay_sample = get_buf_index(inst, 0);
+ inst->min_delay_sample = get_runsbuf_index(inst, -inst->runs_samples);
- for (i = 1; i < inst->n_samples; i++) {
- index = get_buf_index(inst, i);
+ for (i = -inst->runs_samples + 1; i < inst->n_samples; i++) {
+ index = get_runsbuf_index(inst, i);
if (inst->peer_delays[index] < inst->peer_delays[inst->min_delay_sample])
inst->min_delay_sample = index;
}
}
/* ================================================== */
+/* This function estimates asymmetry of network jitter on the path to the
+ source as a slope of offset against network delay in multiple linear
+ regression. If the asymmetry is significant and its sign doesn't change
+ frequently, the measured offsets (which are used later to estimate the
+ offset and frequency of the clock) are corrected to correspond to the
+ minimum network delay. This can significantly improve the accuracy and
+ stability of the estimated offset and frequency. */
+
+static void
+correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
+{
+ double asymmetry, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
+ int i, n;
+
+ /* Don't try to estimate the asymmetry with reference clocks */
+ if (!inst->ip_addr)
+ return;
+
+ n = inst->runs_samples + inst->n_samples;
+
+ for (i = 0; i < n; i++)
+ delays[i] = inst->peer_delays[get_runsbuf_index(inst, i - inst->runs_samples)] -
+ inst->peer_delays[inst->min_delay_sample];
+
+ /* Reset the counter when the regression fails or the sign changes */
+ if (!RGR_MultipleRegress(times_back, delays, offsets, n, &asymmetry) ||
+ asymmetry * inst->asymmetry_run < 0.0) {
+ inst->asymmetry_run = 0;
+ inst->asymmetry = 0.0;
+ return;
+ }
+
+ asymmetry = CLAMP(-MAX_ASYMMETRY, asymmetry, MAX_ASYMMETRY);
+
+ if (asymmetry <= -MIN_ASYMMETRY && inst->asymmetry_run > -MAX_ASYMMETRY_RUN)
+ inst->asymmetry_run--;
+ else if (asymmetry >= MIN_ASYMMETRY && inst->asymmetry_run < MAX_ASYMMETRY_RUN)
+ inst->asymmetry_run++;
+
+ if (abs(inst->asymmetry_run) < MIN_ASYMMETRY_RUN)
+ return;
+
+ /* Correct the offsets */
+ for (i = 0; i < n; i++)
+ offsets[i] -= asymmetry * delays[i];
+
+ inst->asymmetry = asymmetry;
+}
+
+/* ================================================== */
/* This defines the assumed ratio between the standard deviation of
the samples and the peer distance as measured from the round trip
@@ -425,7 +495,8 @@ SST_DoNewRegression(SST_Stats inst)
for (i = 0, mean_distance = 0.0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
j = get_buf_index(inst, i);
- peer_distances[i] = 0.5 * inst->peer_delays[j] + inst->peer_dispersions[j];
+ peer_distances[i] = 0.5 * inst->peer_delays[get_runsbuf_index(inst, i)] +
+ inst->peer_dispersions[j];
mean_distance += peer_distances[i];
if (peer_distances[i] < min_distance) {
min_distance = peer_distances[i];
@@ -445,6 +516,8 @@ SST_DoNewRegression(SST_Stats inst)
}
}
+ correct_asymmetry(inst, times_back, offsets);
+
inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples,
offsets + inst->runs_samples, weights,
inst->n_samples, inst->runs_samples,
@@ -463,7 +536,7 @@ SST_DoNewRegression(SST_Stats inst)
inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample];
inst->estimated_offset_sd = est_intercept_sd;
- inst->variance = est_var;
+ inst->std_dev = sqrt(est_var);
inst->nruns = nruns;
if (inst->skew < MIN_SKEW)
@@ -471,18 +544,20 @@ SST_DoNewRegression(SST_Stats inst)
stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
+ DEBUG_LOG(LOGF_SourceStats, "off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
+ inst->estimated_offset, inst->estimated_frequency, inst->skew,
+ inst->n_samples, best_start, inst->nruns,
+ inst->asymmetry, inst->asymmetry_run);
+
if (logfileid != -1) {
- LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d",
+ LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d %5.2f",
UTI_TimeToLogForm(inst->offset_time.tv_sec),
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid),
- sqrt(inst->variance),
- inst->estimated_offset,
- inst->estimated_offset_sd,
- inst->estimated_frequency,
- inst->skew,
- stress,
- inst->n_samples,
- best_start, nruns);
+ inst->std_dev,
+ inst->estimated_offset, inst->estimated_offset_sd,
+ inst->estimated_frequency, inst->skew, stress,
+ inst->n_samples, best_start, inst->nruns,
+ inst->asymmetry);
}
times_back_start = inst->runs_samples + best_start;
@@ -524,12 +599,12 @@ SST_GetFrequencyRange(SST_Stats inst,
/* ================================================== */
void
-SST_GetSelectionData(SST_Stats inst, struct timeval *now,
+SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
- double *variance,
+ double *std_dev,
double *first_sample_ago,
double *last_sample_ago,
int *select_ok)
@@ -546,9 +621,9 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
j = get_buf_index(inst, inst->best_single_sample);
*stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
- *variance = inst->variance;
+ *std_dev = inst->std_dev;
- UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[i]);
+ sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency;
*root_distance = 0.5 * inst->root_delays[j] +
inst->root_dispersions[j] + sample_elapsed * inst->skew;
@@ -560,10 +635,10 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
double average_offset, elapsed;
int average_ok;
/* average_ok ignored for now */
- UTI_DiffTimevalsToDouble(&elapsed, now, &(inst->offset_time));
+ elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed;
if (fabs(average_offset - offset) <=
- inst->peer_dispersions[j] + 0.5 * inst->peer_delays[j]) {
+ inst->peer_dispersions[j] + 0.5 * inst->peer_delays[i]) {
average_ok = 1;
} else {
average_ok = 0;
@@ -571,21 +646,21 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
#endif
i = get_runsbuf_index(inst, 0);
- UTI_DiffTimevalsToDouble(first_sample_ago, now, &inst->sample_times[i]);
+ *first_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
i = get_runsbuf_index(inst, inst->n_samples - 1);
- UTI_DiffTimevalsToDouble(last_sample_ago, now, &inst->sample_times[i]);
+ *last_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
*select_ok = inst->regression_ok;
- DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f var=%f first_ago=%f last_ago=%f selok=%d",
- inst->n_samples, offset, *root_distance, *variance,
+ DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
+ inst->n_samples, offset, *root_distance, *std_dev,
*first_sample_ago, *last_sample_ago, *select_ok);
}
/* ================================================== */
void
-SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
+SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *root_delay, double *root_dispersion)
@@ -605,7 +680,7 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
*skew = inst->skew;
*root_delay = inst->root_delays[j];
- UTI_DiffTimevalsToDouble(&elapsed_sample, &inst->offset_time, &inst->sample_times[i]);
+ elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
@@ -616,11 +691,11 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
/* ================================================== */
void
-SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset)
+SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset)
{
int m, i;
double delta_time;
- struct timeval *sample, prev;
+ struct timespec *sample, prev;
double prev_offset, prev_freq;
if (!inst->n_samples)
@@ -628,9 +703,9 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
for (m = -inst->runs_samples; m < inst->n_samples; m++) {
i = get_runsbuf_index(inst, m);
- sample = &(inst->sample_times[i]);
+ sample = &inst->sample_times[i];
prev = *sample;
- UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
+ UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
inst->offsets[i] += delta_time;
}
@@ -638,14 +713,14 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
prev = inst->offset_time;
prev_offset = inst->estimated_offset;
prev_freq = inst->estimated_frequency;
- UTI_AdjustTimeval(&(inst->offset_time), when, &(inst->offset_time),
+ UTI_AdjustTimespec(&inst->offset_time, when, &inst->offset_time,
&delta_time, dfreq, doffset);
inst->estimated_offset += delta_time;
inst->estimated_frequency = (inst->estimated_frequency - dfreq) / (1.0 - dfreq);
DEBUG_LOG(LOGF_SourceStats, "n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f",
inst->n_samples, inst->runs_samples,
- UTI_TimevalToString(&prev), UTI_TimevalToString(&(inst->offset_time)),
+ UTI_TimespecToString(&prev), UTI_TimespecToString(&inst->offset_time),
prev_offset, inst->estimated_offset,
1.0e6 * prev_freq, 1.0e6 * inst->estimated_frequency);
}
@@ -667,7 +742,7 @@ SST_AddDispersion(SST_Stats inst, double dispersion)
/* ================================================== */
double
-SST_PredictOffset(SST_Stats inst, struct timeval *when)
+SST_PredictOffset(SST_Stats inst, struct timespec *when)
{
double elapsed;
@@ -681,7 +756,7 @@ SST_PredictOffset(SST_Stats inst, struct timeval *when)
return 0.0;
}
} else {
- UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time);
+ elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
return inst->estimated_offset + elapsed * inst->estimated_frequency;
}
@@ -701,20 +776,20 @@ SST_MinRoundTripDelay(SST_Stats inst)
int
SST_IsGoodSample(SST_Stats inst, double offset, double delay,
- double max_delay_dev_ratio, double clock_error, struct timeval *when)
+ double max_delay_dev_ratio, double clock_error, struct timespec *when)
{
double elapsed, allowed_increase, delay_increase;
if (inst->n_samples < 3)
return 1;
- UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time);
+ elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
/* Require that the ratio of the increase in delay from the minimum to the
standard deviation is less than max_delay_dev_ratio. In the allowed
increase in delay include also skew and clock_error. */
- allowed_increase = sqrt(inst->variance) * max_delay_dev_ratio +
+ allowed_increase = inst->std_dev * max_delay_dev_ratio +
elapsed * (inst->skew + clock_error);
delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0;
@@ -750,12 +825,18 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
i = get_runsbuf_index(inst, m);
j = get_buf_index(inst, m);
- fprintf(out, "%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
- (unsigned long) inst->sample_times[i].tv_sec,
- (unsigned long) inst->sample_times[i].tv_usec,
+ fprintf(out,
+#ifdef HAVE_LONG_TIME_T
+ "%08"PRIx64" %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
+ (uint64_t)inst->sample_times[i].tv_sec,
+#else
+ "%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
+ (unsigned long)inst->sample_times[i].tv_sec,
+#endif
+ (unsigned long)inst->sample_times[i].tv_nsec / 1000,
inst->offsets[i],
inst->orig_offsets[j],
- inst->peer_delays[j],
+ inst->peer_delays[i],
inst->peer_dispersions[j],
inst->root_delays[j],
inst->root_dispersions[j],
@@ -771,22 +852,30 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
int
SST_LoadFromFile(SST_Stats inst, FILE *in)
{
- int i, line_number;
+#ifdef HAVE_LONG_TIME_T
+ uint64_t sec;
+#else
+ unsigned long sec;
+#endif
+ unsigned long usec;
+ int i;
char line[1024];
- unsigned long sec, usec;
double weight;
assert(!inst->n_samples);
if (fgets(line, sizeof(line), in) &&
sscanf(line, "%d", &inst->n_samples) == 1 &&
- inst->n_samples > 0 && inst->n_samples <= MAX_SAMPLES) {
-
- line_number = 2;
+ inst->n_samples >= 0 && inst->n_samples <= MAX_SAMPLES) {
for (i=0; i<inst->n_samples; i++) {
if (!fgets(line, sizeof(line), in) ||
- (sscanf(line, "%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
+ (sscanf(line,
+#ifdef HAVE_LONG_TIME_T
+ "%"SCNx64"%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
+#else
+ "%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
+#endif
&(sec), &(usec),
&(inst->offsets[i]),
&(inst->orig_offsets[i]),
@@ -799,40 +888,40 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
/* This is the branch taken if the read FAILED */
- LOG(LOGS_WARN, LOGF_SourceStats, "Failed to read data from line %d of dump file", line_number);
inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0;
} else {
/* This is the branch taken if the read is SUCCESSFUL */
inst->sample_times[i].tv_sec = sec;
- inst->sample_times[i].tv_usec = usec;
-
- line_number++;
+ inst->sample_times[i].tv_nsec = 1000 * usec;
+ UTI_NormaliseTimespec(&inst->sample_times[i]);
}
}
} else {
- LOG(LOGS_WARN, LOGF_SourceStats, "Could not read number of samples from dump file");
inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0;
}
+ if (!inst->n_samples)
+ return 1;
+
inst->last_sample = inst->n_samples - 1;
inst->runs_samples = 0;
find_min_delay_sample(inst);
+ SST_DoNewRegression(inst);
return 1;
-
}
/* ================================================== */
void
-SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now)
+SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now)
{
int i, j;
- struct timeval ago;
+ struct timespec ago;
if (inst->n_samples > 0) {
i = get_runsbuf_index(inst, inst->n_samples - 1);
@@ -842,7 +931,7 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j];
- UTI_DiffTimevals(&ago, now, &inst->sample_times[i]);
+ UTI_DiffTimespecs(&ago, now, &inst->sample_times[i]);
report->latest_meas_ago = ago.tv_sec;
} else {
report->latest_meas_ago = (uint32_t)-1;
@@ -864,7 +953,7 @@ SST_Samples(SST_Stats inst)
/* ================================================== */
void
-SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timeval *now)
+SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
{
double dspan;
double elapsed, sample_elapsed;
@@ -876,15 +965,15 @@ SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct ti
if (inst->n_samples > 1) {
li = get_runsbuf_index(inst, inst->n_samples - 1);
lj = get_buf_index(inst, inst->n_samples - 1);
- UTI_DiffTimevalsToDouble(&dspan, &inst->sample_times[li],
+ dspan = UTI_DiffTimespecsToDouble(&inst->sample_times[li],
&inst->sample_times[get_runsbuf_index(inst, 0)]);
report->span_seconds = (unsigned long) (dspan + 0.5);
if (inst->n_samples > 3) {
- UTI_DiffTimevalsToDouble(&elapsed, now, &inst->offset_time);
+ elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
bi = get_runsbuf_index(inst, inst->best_single_sample);
bj = get_buf_index(inst, inst->best_single_sample);
- UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[bi]);
+ sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[bi]);
report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency;
report->est_offset_err = (inst->estimated_offset_sd +
sample_elapsed * inst->skew +
@@ -901,7 +990,15 @@ SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct ti
report->resid_freq_ppm = 1.0e6 * inst->estimated_frequency;
report->skew_ppm = 1.0e6 * inst->skew;
- report->sd = sqrt(inst->variance);
+ report->sd = inst->std_dev;
+}
+
+/* ================================================== */
+
+double
+SST_GetJitterAsymmetry(SST_Stats inst)
+{
+ return inst->asymmetry;
}
/* ================================================== */
diff --git a/sourcestats.h b/sourcestats.h
index cba0a65..48f73b3 100644
--- a/sourcestats.h
+++ b/sourcestats.h
@@ -61,7 +61,7 @@ extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
stratum is the stratum of the source from which the sample came.
*/
-extern void SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
+extern void SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
/* This function runs the linear regression operation on the data. It
finds the set of most recent samples that give the tightest
@@ -77,7 +77,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */
extern void
-SST_GetSelectionData(SST_Stats inst, struct timeval *now,
+SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit,
double *offset_hi_limit,
@@ -89,7 +89,7 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
/* Get data needed when setting up tracking on this source */
extern void
-SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
+SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *root_delay, double *root_dispersion);
@@ -110,7 +110,7 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
*/
-extern void SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset);
+extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset);
/* This routine is called when an indeterminate offset is introduced
into the local time. */
@@ -119,7 +119,7 @@ extern void SST_AddDispersion(SST_Stats inst, double dispersion);
/* Predict the offset of the local clock relative to a given source at
a given local cooked time. Positive indicates local clock is FAST
relative to reference. */
-extern double SST_PredictOffset(SST_Stats inst, struct timeval *when);
+extern double SST_PredictOffset(SST_Stats inst, struct timespec *when);
/* Find the minimum round trip delay in the register */
extern double SST_MinRoundTripDelay(SST_Stats inst);
@@ -127,17 +127,19 @@ extern double SST_MinRoundTripDelay(SST_Stats inst);
/* This routine determines if a new sample is good enough that it should be
accumulated */
extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay,
- double max_delay_dev_ratio, double clock_error, struct timeval *when);
+ double max_delay_dev_ratio, double clock_error, struct timespec *when);
extern void SST_SaveToFile(SST_Stats inst, FILE *out);
extern int SST_LoadFromFile(SST_Stats inst, FILE *in);
-extern void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now);
+extern void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now);
-extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timeval *now);
+extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now);
extern int SST_Samples(SST_Stats inst);
+extern double SST_GetJitterAsymmetry(SST_Stats inst);
+
#endif /* GOT_SOURCESTATS_H */
diff --git a/srcparams.h b/srcparams.h
index 42a1555..bdf13a8 100644
--- a/srcparams.h
+++ b/srcparams.h
@@ -42,22 +42,24 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
+ int interleaved;
int sel_options;
uint32_t authkey;
double max_delay;
double max_delay_ratio;
double max_delay_dev_ratio;
+ double offset;
} SourceParameters;
#define SRC_DEFAULT_PORT 123
#define SRC_DEFAULT_MINPOLL 6
#define SRC_DEFAULT_MAXPOLL 10
-#define SRC_DEFAULT_PRESEND_MINPOLL 0
+#define SRC_DEFAULT_PRESEND_MINPOLL 100
#define SRC_DEFAULT_MAXDELAY 3.0
#define SRC_DEFAULT_MAXDELAYRATIO 0.0
#define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0
#define SRC_DEFAULT_MINSTRATUM 0
-#define SRC_DEFAULT_POLLTARGET 6
+#define SRC_DEFAULT_POLLTARGET 8
#define SRC_DEFAULT_MAXSOURCES 4
#define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1)
diff --git a/stubs.c b/stubs.c
index a5e50b6..9bc127c 100644
--- a/stubs.c
+++ b/stubs.c
@@ -38,6 +38,7 @@
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_sources.h"
+#include "ntp_signd.h"
#include "privops.h"
#include "refclock.h"
#include "sched.h"
@@ -291,11 +292,17 @@ NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
}
void
-NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
+NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
memset(report, 0, sizeof (*report));
}
+int
+NSR_GetNTPReport(RPT_NTPReport *report)
+{
+ return 0;
+}
+
void
NSR_GetActivityReport(RPT_ActivityReport *report)
{
@@ -361,9 +368,35 @@ RCL_StartRefclocks(void)
}
void
-RCL_ReportSource(RPT_SourceReport *report, struct timeval *now)
+RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
memset(report, 0, sizeof (*report));
}
#endif /* !FEAT_REFCLOCK */
+
+#ifndef FEAT_SIGND
+
+void
+NSD_Initialise(void)
+{
+}
+
+void
+NSD_Finalise(void)
+{
+}
+
+int
+NSD_GetAuthDelay(uint32_t key_id)
+{
+ return 0;
+}
+
+int
+NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
+{
+ return 0;
+}
+
+#endif /* !FEAT_SIGND */
diff --git a/sys_generic.c b/sys_generic.c
index 6a30f4c..90eaa0c 100644
--- a/sys_generic.c
+++ b/sys_generic.c
@@ -71,7 +71,7 @@ static double offset_register;
static double slew_freq;
/* Time (raw) of last update of slewing frequency and offset */
-static struct timeval slew_start;
+static struct timespec slew_start;
/* Limits for the slew timeout */
#define MIN_SLEW_TIMEOUT 1.0
@@ -106,7 +106,7 @@ static void update_slew(void);
/* Adjust slew_start on clock step */
static void
-handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
+handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type == LCL_ChangeUnknownStep) {
@@ -115,7 +115,7 @@ handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
offset_register = 0.0;
update_slew();
} else if (change_type == LCL_ChangeStep) {
- UTI_AddDoubleToTimeval(&slew_start, -doffset, &slew_start);
+ UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
}
}
@@ -138,7 +138,7 @@ start_fastslew(void)
/* ================================================== */
static void
-stop_fastslew(struct timeval *now)
+stop_fastslew(struct timespec *now)
{
double corr;
@@ -169,7 +169,7 @@ clamp_freq(double freq)
static void
update_slew(void)
{
- struct timeval now, end_of_slew;
+ struct timespec now, end_of_slew;
double old_slew_freq, total_freq, corr_freq, duration;
/* Remove currently running timeout */
@@ -178,7 +178,7 @@ update_slew(void)
LCL_ReadRawTime(&now);
/* Adjust the offset register by achieved slew */
- UTI_DiffTimevalsToDouble(&duration, &now, &slew_start);
+ duration = UTI_DiffTimespecsToDouble(&now, &slew_start);
offset_register -= slew_freq * duration;
stop_fastslew(&now);
@@ -242,7 +242,7 @@ update_slew(void)
}
/* Restart timer for the next update */
- UTI_AddDoubleToTimeval(&now, duration, &end_of_slew);
+ UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
slew_start = now;
@@ -294,12 +294,12 @@ accrue_offset(double offset, double corr_rate)
/* Determine the correction to generate the cooked time for given raw time */
static void
-offset_convert(struct timeval *raw,
+offset_convert(struct timespec *raw,
double *corr, double *err)
{
double duration, fastslew_corr, fastslew_err;
- UTI_DiffTimevalsToDouble(&duration, raw, &slew_start);
+ duration = UTI_DiffTimespecsToDouble(raw, &slew_start);
if (drv_get_offset_correction && fastslew_active) {
drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
@@ -324,19 +324,21 @@ offset_convert(struct timeval *raw,
static int
apply_step_offset(double offset)
{
- struct timeval old_time, new_time;
+ struct timespec old_time, new_time;
+ struct timeval new_time_tv;
double err;
LCL_ReadRawTime(&old_time);
- UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+ UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
+ UTI_TimespecToTimeval(&new_time, &new_time_tv);
- if (PRV_SetTime(&new_time, NULL) < 0) {
+ if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed");
return 0;
}
LCL_ReadRawTime(&old_time);
- UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
+ err = UTI_DiffTimespecsToDouble(&old_time, &new_time);
lcl_InvokeDispersionNotifyHandlers(fabs(err));
@@ -403,7 +405,7 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
void
SYS_Generic_Finalise(void)
{
- struct timeval now;
+ struct timespec now;
/* Must *NOT* leave a slew running - clock could drift way off
if the daemon is not restarted */
diff --git a/sys_linux.c b/sys_linux.c
index c802da9..7133027 100644
--- a/sys_linux.c
+++ b/sys_linux.c
@@ -51,7 +51,7 @@
#include <sys/prctl.h>
#include <seccomp.h>
#include <termios.h>
-#ifdef FEAT_PHC
+#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_PPS
@@ -60,6 +60,9 @@
#ifdef FEAT_RTC
#include <linux/rtc.h>
#endif
+#ifdef HAVE_LINUX_TIMESTAMPING
+#include <linux/sockios.h>
+#endif
#endif
#include "sys_linux.h"
@@ -271,6 +274,22 @@ kernelvercmp(int major1, int minor1, int patch1,
}
/* ================================================== */
+
+static void
+get_kernel_version(int *major, int *minor, int *patch)
+{
+ struct utsname uts;
+
+ if (uname(&uts) < 0)
+ LOG_FATAL(LOGF_SysLinux, "uname() failed");
+
+ *patch = 0;
+ if (sscanf(uts.release, "%d.%d.%d", major, minor, patch) < 2)
+ LOG_FATAL(LOGF_SysLinux, "Could not parse kernel version");
+}
+
+/* ================================================== */
+
/* Compute the scaling to use on any frequency we set, according to
the vintage of the Linux kernel being used. */
@@ -278,7 +297,6 @@ static void
get_version_specific_details(void)
{
int major, minor, patch;
- struct utsname uts;
hz = get_hz();
@@ -293,15 +311,7 @@ get_version_specific_details(void)
(CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
tick_update_hz = 100;
- if (uname(&uts) < 0) {
- LOG_FATAL(LOGF_SysLinux, "Cannot uname(2) to get kernel version, sorry.");
- }
-
- patch = 0;
- if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) < 2) {
- LOG_FATAL(LOGF_SysLinux, "Cannot read information from uname, sorry");
- }
-
+ get_kernel_version(&major, &minor, &patch);
DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
@@ -452,8 +462,8 @@ SYS_Linux_EnableSystemCallFilter(int level)
{
const int syscalls[] = {
/* Clock */
- SCMP_SYS(adjtimex), SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday),
- SCMP_SYS(time),
+ SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday),
+ SCMP_SYS(settimeofday), SCMP_SYS(time),
/* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getrlimit),
SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigprocmask),
@@ -463,17 +473,17 @@ SYS_Linux_EnableSystemCallFilter(int level)
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(fstat), SCMP_SYS(fstat64), SCMP_SYS(lseek), SCMP_SYS(rename),
- SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64),
- SCMP_SYS(unlink),
+ 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),
/* Socket */
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
- SCMP_SYS(recvfrom), SCMP_SYS(recvmsg), SCMP_SYS(sendmmsg),
- SCMP_SYS(sendmsg), SCMP_SYS(sendto),
+ SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
+ 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(pipe),
+ SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe),
SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
SCMP_SYS(set_robust_list), SCMP_SYS(write),
/* Miscellaneous */
@@ -493,14 +503,17 @@ SYS_Linux_EnableSystemCallFilter(int level)
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
- { SOL_SOCKET, SO_TIMESTAMP },
+ { SOL_SOCKET, SO_TIMESTAMP }, { SOL_SOCKET, SO_TIMESTAMPNS },
+#ifdef HAVE_LINUX_TIMESTAMPING
+ { SOL_SOCKET, SO_SELECT_ERR_QUEUE }, { SOL_SOCKET, SO_TIMESTAMPING },
+#endif
};
const static int fcntls[] = { F_GETFD, F_SETFD };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,
-#ifdef FEAT_PPS
+#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_SYS_OFFSET,
#endif
#ifdef FEAT_PPS
@@ -509,6 +522,9 @@ SYS_Linux_EnableSystemCallFilter(int level)
#ifdef FEAT_RTC
RTC_RD_TIME, RTC_SET_TIME, RTC_UIE_ON, RTC_UIE_OFF,
#endif
+#ifdef HAVE_LINUX_TIMESTAMPING
+ SIOCETHTOOL,
+#endif
};
scmp_filter_ctx *ctx;
@@ -633,3 +649,15 @@ void SYS_Linux_MemLockAll(int LockAll)
}
}
#endif /* HAVE_MLOCKALL */
+
+/* ================================================== */
+
+int
+SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
+{
+ int major, minor, patch;
+
+ get_kernel_version(&major, &minor, &patch);
+
+ return kernelvercmp(req_major, req_minor, 0, major, minor, patch) <= 0;
+}
diff --git a/sys_linux.h b/sys_linux.h
index f2c6d59..d4e52ad 100644
--- a/sys_linux.h
+++ b/sys_linux.h
@@ -39,4 +39,6 @@ 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);
+
#endif /* GOT_SYS_LINUX_H */
diff --git a/sys_macosx.c b/sys_macosx.c
index 46e439e..35f03d6 100644
--- a/sys_macosx.c
+++ b/sys_macosx.c
@@ -23,7 +23,7 @@
=======================================================================
- Driver file for the MacOS X operating system.
+ Driver file for the macOS operating system.
*/
@@ -39,6 +39,7 @@
#include "sys_macosx.h"
#include "conf.h"
+#include "local.h"
#include "localp.h"
#include "logging.h"
#include "sched.h"
@@ -49,13 +50,13 @@
/* This register contains the number of seconds by which the local
clock was estimated to be fast of reference time at the epoch when
- gettimeofday() returned T0 */
+ LCL_ReadRawTime() returned T0 */
static double offset_register;
/* This register contains the epoch to which the offset is referenced */
-static struct timeval T0;
+static struct timespec T0;
/* This register contains the current estimate of the system
frequency, in absolute (NOT ppm) */
@@ -79,7 +80,7 @@ static double adjustment_requested;
static double drift_removal_interval;
static double current_drift_removal_interval;
-static struct timeval Tdrift;
+static struct timespec Tdrift;
/* weighting applied to error in calculating drift_removal_interval */
#define ERROR_WEIGHT (0.5)
@@ -91,7 +92,7 @@ static struct timeval Tdrift;
/* RTC synchronisation - once an hour */
-static struct timeval last_rtc_sync;
+static struct timespec last_rtc_sync;
#define RTC_SYNC_INTERVAL (60 * 60.0)
/* ================================================== */
@@ -107,9 +108,7 @@ clock_initialise(void)
drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
current_drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
- if (gettimeofday(&T0, NULL) < 0) {
- LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
- }
+ LCL_ReadRawTime(&T0);
Tdrift = T0;
last_rtc_sync = T0;
@@ -135,21 +134,19 @@ static void
start_adjust(void)
{
struct timeval newadj, oldadj;
- struct timeval T1;
+ struct timespec T1;
double elapsed, accrued_error, predicted_error, drift_removal_elapsed;
double adjust_required;
double rounding_error;
double old_adjust_remaining;
/* Determine the amount of error built up since the last adjustment */
- if (gettimeofday(&T1, NULL) < 0) {
- LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
- }
+ LCL_ReadRawTime(&T1);
- UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ elapsed = UTI_DiffTimespecsToDouble(&T1, &T0);
accrued_error = elapsed * current_freq;
- UTI_DiffTimevalsToDouble(&drift_removal_elapsed, &T1, &Tdrift);
+ drift_removal_elapsed = UTI_DiffTimespecsToDouble(&T1, &Tdrift);
/* To allow for the clock being stepped either forward or backwards, clamp
the elapsed time to bounds [ 0.0, current_drift_removal_interval ] */
@@ -165,14 +162,14 @@ start_adjust(void)
adjust_required = - (accrued_error + offset_register + predicted_error);
UTI_DoubleToTimeval(adjust_required, &newadj);
- UTI_TimevalToDouble(&newadj, &adjustment_requested);
+ adjustment_requested = UTI_TimevalToDouble(&newadj);
rounding_error = adjust_required - adjustment_requested;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
- UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
+ old_adjust_remaining = UTI_TimevalToDouble(&oldadj);
offset_register = rounding_error - old_adjust_remaining - predicted_error;
@@ -184,7 +181,7 @@ start_adjust(void)
static void
stop_adjust(void)
{
- struct timeval T1;
+ struct timespec T1;
struct timeval zeroadj, remadj;
double adjustment_remaining, adjustment_achieved;
double elapsed, elapsed_plus_adjust;
@@ -196,12 +193,10 @@ stop_adjust(void)
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
- if (gettimeofday(&T1, NULL) < 0) {
- LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
- }
+ LCL_ReadRawTime(&T1);
- UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
- UTI_TimevalToDouble(&remadj, &adjustment_remaining);
+ elapsed = UTI_DiffTimespecsToDouble(&T1, &T0);
+ adjustment_remaining = UTI_TimevalToDouble(&remadj);
adjustment_achieved = adjustment_requested - adjustment_remaining;
elapsed_plus_adjust = elapsed - adjustment_achieved;
@@ -233,22 +228,22 @@ accrue_offset(double offset, double corr_rate)
static int
apply_step_offset(double offset)
{
- struct timeval old_time, new_time, T1;
+ struct timespec old_time, new_time, T1;
+ struct timeval new_time_tv;
stop_adjust();
- if (gettimeofday(&old_time, NULL) < 0) {
- LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
- }
+ LCL_ReadRawTime(&old_time);
- UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+ UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
+ UTI_TimespecToTimeval(&new_time, &new_time_tv);
- if (PRV_SetTime(&new_time, NULL) < 0) {
+ if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
return 0;
}
- UTI_AddDoubleToTimeval(&T0, -offset, &T1);
+ UTI_AddDoubleToTimespec(&T0, -offset, &T1);
T0 = T1;
start_adjust();
@@ -279,7 +274,7 @@ read_frequency(void)
/* ================================================== */
static void
-get_offset_correction(struct timeval *raw,
+get_offset_correction(struct timespec *raw,
double *corr, double *err)
{
stop_adjust();
@@ -311,9 +306,7 @@ drift_removal_timeout(SCH_ArbitraryArgument not_used)
stop_adjust();
- if (gettimeofday(&Tdrift, NULL) < 0) {
- LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
- }
+ LCL_ReadRawTime(&Tdrift);
current_drift_removal_interval = drift_removal_interval;
@@ -336,11 +329,11 @@ set_sync_status(int synchronised, double est_error, double max_error)
drift_removal_interval = MAX(drift_removal_interval, DRIFT_REMOVAL_INTERVAL);
} else {
if (CNF_GetRtcSync()) {
- struct timeval now;
+ struct timespec now;
double rtc_sync_elapsed;
SCH_GetLastEventTime(NULL, NULL, &now);
- UTI_DiffTimevalsToDouble(&rtc_sync_elapsed, &now, &last_rtc_sync);
+ rtc_sync_elapsed = UTI_DiffTimespecsToDouble(&now, &last_rtc_sync);
if (fabs(rtc_sync_elapsed) >= RTC_SYNC_INTERVAL) {
/* update the RTC by applying a step of 0.0 secs */
apply_step_offset(0.0);
diff --git a/sys_macosx.h b/sys_macosx.h
index b9114ff..5555616 100644
--- a/sys_macosx.h
+++ b/sys_macosx.h
@@ -23,7 +23,7 @@
=======================================================================
- Header file for MacOS X driver
+ Header file for macOS driver
*/
diff --git a/sys_netbsd.c b/sys_netbsd.c
index 407be84..74cadbc 100644
--- a/sys_netbsd.c
+++ b/sys_netbsd.c
@@ -60,6 +60,7 @@ static void
accrue_offset(double offset, double corr_rate)
{
struct timeval newadj, oldadj;
+ double doldadj;
UTI_DoubleToTimeval(-offset, &newadj);
@@ -67,9 +68,9 @@ accrue_offset(double offset, double corr_rate)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
/* Add the old remaining adjustment if not zero */
- UTI_TimevalToDouble(&oldadj, &offset);
- if (offset != 0.0) {
- UTI_AddDoubleToTimeval(&newadj, offset, &newadj);
+ doldadj = UTI_TimevalToDouble(&oldadj);
+ if (doldadj != 0.0) {
+ UTI_DoubleToTimeval(-offset + doldadj, &newadj);
if (PRV_AdjustTime(&newadj, NULL) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
}
@@ -78,7 +79,7 @@ accrue_offset(double offset, double corr_rate)
/* ================================================== */
static void
-get_offset_correction(struct timeval *raw,
+get_offset_correction(struct timespec *raw,
double *corr, double *err)
{
struct timeval remadj;
@@ -87,7 +88,7 @@ get_offset_correction(struct timeval *raw,
if (PRV_AdjustTime(NULL, &remadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
- UTI_TimevalToDouble(&remadj, &adjustment_remaining);
+ adjustment_remaining = UTI_TimevalToDouble(&remadj);
*corr = adjustment_remaining;
if (err) {
diff --git a/tempcomp.c b/tempcomp.c
index 07cbbb9..555fa24 100644
--- a/tempcomp.c
+++ b/tempcomp.c
@@ -95,7 +95,7 @@ read_timeout(void *arg)
DEBUG_LOG(LOGF_TempComp, "tempcomp updated to %f for %f", comp, temp);
if (logfileid != -1) {
- struct timeval now;
+ struct timespec now;
LCL_ReadCookedTime(&now, NULL);
LOG_FileWrite(logfileid, "%s %11.4e %11.4e",
@@ -135,7 +135,7 @@ read_points(const char *filename)
while (fgets(line, sizeof (line), f)) {
p = (struct Point *)ARR_GetNewElement(points);
if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) {
- LOG_FATAL(LOGF_Configure, "Could not read tempcomp point from %s", filename);
+ LOG_FATAL(LOGF_TempComp, "Could not read tempcomp point from %s", filename);
break;
}
}
@@ -143,7 +143,7 @@ read_points(const char *filename)
fclose(f);
if (ARR_GetSize(points) < 2)
- LOG_FATAL(LOGF_Configure, "Not enough points in %s", filename);
+ LOG_FATAL(LOGF_TempComp, "Not enough points in %s", filename);
}
void
diff --git a/test/compilation/001-features b/test/compilation/001-features
index 9da21d2..d61c07f 100755
--- a/test/compilation/001-features
+++ b/test/compilation/001-features
@@ -6,6 +6,7 @@ cd ../..
for opts in \
"--enable-debug" \
+ "--enable-ntp-signd" \
"--enable-scfilter" \
"--disable-asyncdns" \
"--disable-ipv6" \
@@ -16,6 +17,8 @@ for opts in \
"--disable-cmdmon" \
"--disable-ntp" \
"--disable-refclock" \
+ "--disable-timestamping" \
+ "--disable-timestamping --disable-ntp" \
"--disable-cmdmon --disable-ntp" \
"--disable-cmdmon --disable-refclock" \
"--disable-cmdmon --disable-ntp --disable-refclock"
diff --git a/test/compilation/002-scanbuild b/test/compilation/002-scanbuild
new file mode 100755
index 0000000..085cc65
--- /dev/null
+++ b/test/compilation/002-scanbuild
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+cd ../..
+
+for opts in \
+ "--host-system=Linux" \
+ "--host-system=NetBSD" \
+ "--host-system=FreeBSD" \
+ "--without-nss" \
+ "--without-tomcrypt --without-nss"
+do
+ ./configure $opts
+ scan-build make "$@" || exit 1
+done
diff --git a/test/simulation/010-multrecv b/test/simulation/010-multrecv
new file mode 100755
index 0000000..8adfab6
--- /dev/null
+++ b/test/simulation/010-multrecv
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+. ./test.common
+
+export CLKNETSIM_RECV_MULTIPLY=4
+
+test_start "multiple received packets"
+
+limit=50000
+client_server_options="minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/011-asymjitter b/test/simulation/011-asymjitter
new file mode 100755
index 0000000..18e6ec1
--- /dev/null
+++ b/test/simulation/011-asymjitter
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "asymmetric jitter"
+
+jitter=5e-4
+jitter_asymmetry=0.47
+limit=100000
+max_sync_time=2000
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/105-ntpauth b/test/simulation/105-ntpauth
index 5cfdf32..4c77f10 100755
--- a/test/simulation/105-ntpauth
+++ b/test/simulation/105-ntpauth
@@ -37,13 +37,15 @@ for hash in $hashes; do
echo "$key" >> tmp/client.keys
done
-for key in $(seq $keys); do
- client_server_options="key $key"
- run_test || test_fail
- check_chronyd_exit || test_fail
- check_source_selection || test_fail
- check_packet_interval || test_fail
- check_sync || test_fail
+for version in 3 4; do
+ for key in $(seq $keys); do
+ client_server_options="version $version key $key"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+ done
done
server_conf=""
diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc
index 34ee283..690026e 100755
--- a/test/simulation/110-chronyc
+++ b/test/simulation/110-chronyc
@@ -11,7 +11,7 @@ sourcestats"
run_test || test_fail
check_chronyd_exit || test_fail
-check_chronyc_output "^Reference ID : 192\.168\.123\.1 \(192\.168\.123\.1\)
+check_chronyc_output "^Reference ID : C0A87B01 \(192\.168\.123\.1\)
Stratum : 2
Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time
diff --git a/test/simulation/113-leapsecond b/test/simulation/113-leapsecond
index dcf1439..5b9758f 100755
--- a/test/simulation/113-leapsecond
+++ b/test/simulation/113-leapsecond
@@ -7,6 +7,7 @@ export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
leap=$[2 * 24 * 3600]
limit=$[4 * 24 * 3600]
+client_start=$[2 * 3600]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
refclock_jitter=1e-9
@@ -27,14 +28,25 @@ for leapmode in system step slew; do
check_sync || test_fail
done
+client_server_options="trust"
+client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+client_server_options=""
+client_conf="leapsecmode system"
+min_sync_time=230000
+max_sync_time=240000
+
for smoothmode in "" "leaponly"; do
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC
leapsecmode slew
smoothtime 400 0.001 $smoothmode"
- client_conf="leapsecmode system"
- min_sync_time=230000
- max_sync_time=240000
run_test || test_fail
check_chronyd_exit || test_fail
diff --git a/test/simulation/114-presend b/test/simulation/114-presend
index 1371aa8..3113253 100755
--- a/test/simulation/114-presend
+++ b/test/simulation/114-presend
@@ -3,7 +3,7 @@
. ./test.common
test_start "presend option"
-min_sync_time=140
+min_sync_time=136
max_sync_time=260
client_server_options="presend 6 maxdelay 16"
client_conf="maxdistance 10"
diff --git a/test/simulation/119-smoothtime b/test/simulation/119-smoothtime
index 3d39233..a7db99e 100755
--- a/test/simulation/119-smoothtime
+++ b/test/simulation/119-smoothtime
@@ -19,7 +19,8 @@ refclock_offset="(* 10.0 (equal 0.1 (max (sum 1.0) 1000) 1000))"
server_step="(* -10.0 (equal 0.1 (sum 1.0) 1))"
server_strata=1
server_conf="refclock SHM 0 dpoll 4 poll 6
-smoothtime 2000 1"
+smoothtime 2000 1
+maxjitter 10.0"
time_offset=-10
client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100"
diff --git a/test/simulation/122-xleave b/test/simulation/122-xleave
new file mode 100755
index 0000000..e0ce9ec
--- /dev/null
+++ b/test/simulation/122-xleave
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+. ./test.common
+test_start "interleaved mode"
+
+client_server_options="xleave"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+clients=2
+peers=2
+max_sync_time=500
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 2) (equal 0.1 to 1)))"
+
+client_lpeer_options="xleave minpoll 5 maxpoll 5"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+# These checks are expected to fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+for rpoll in 4 5 6; do
+ client_rpeer_options="xleave minpoll $rpoll maxpoll $rpoll"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/test.common b/test/simulation/test.common
index 0a94021..2c28e89 100644
--- a/test/simulation/test.common
+++ b/test/simulation/test.common
@@ -31,6 +31,7 @@ default_time_offset=1e-1
default_freq_offset=1e-4
default_base_delay=1e-4
default_jitter=1e-4
+default_jitter_asymmetry=0.0
default_wander=1e-9
default_refclock_jitter=""
default_refclock_offset=0.0
@@ -154,7 +155,16 @@ get_wander_expr() {
get_delay_expr() {
- echo "(+ $base_delay (* $jitter (exponential)))"
+ local direction=$1 asym
+
+ if [ $jitter_asymmetry == "0.0" ]; then
+ asym=""
+ elif [ $direction = "up" ]; then
+ asym=$(awk "BEGIN {print 1 - 2 * $jitter_asymmetry}")
+ elif [ $direction = "down" ]; then
+ asym=$(awk "BEGIN {print 1 + 2 * $jitter_asymmetry}")
+ fi
+ echo "(+ $base_delay (* $asym $jitter (exponential)))"
}
get_refclock_expr() {
@@ -378,8 +388,8 @@ run_test() {
echo "node${i}_shift_pll = $shift_pll"
for j in $(seq 1 $nodes); do
[ $i -eq $j ] && continue
- echo "node${i}_delay${j} = $(get_delay_expr)"
- echo "node${j}_delay${i} = $(get_delay_expr)"
+ echo "node${i}_delay${j} = $(get_delay_expr up)"
+ echo "node${j}_delay${i} = $(get_delay_expr down)"
done
done > tmp/conf
diff --git a/test/unit/clientlog.c b/test/unit/clientlog.c
index 85e7f19..ee29936 100644
--- a/test/unit/clientlog.c
+++ b/test/unit/clientlog.c
@@ -25,7 +25,7 @@ void
test_unit(void)
{
int i, j, index;
- struct timeval tv;
+ struct timespec ts;
IPAddr ip;
char conf[][100] = {
"clientloglimit 10000",
@@ -44,33 +44,33 @@ test_unit(void)
for (i = 0; i < 500; i++) {
DEBUG_LOG(0, "iteration %d", i);
- tv.tv_sec = (time_t)random() & 0x0fffffff;
- tv.tv_usec = 0;
+ ts.tv_sec = (time_t)random() & 0x0fffffff;
+ ts.tv_nsec = 0;
for (j = 0; j < 1000; j++) {
TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
DEBUG_LOG(0, "address %s", UTI_IPToString(&ip));
if (random() % 2) {
- index = CLG_LogNTPAccess(&ip, &tv);
+ index = CLG_LogNTPAccess(&ip, &ts);
TEST_CHECK(index >= 0);
CLG_LimitNTPResponseRate(index);
} else {
- index = CLG_LogCommandAccess(&ip, &tv);
+ index = CLG_LogCommandAccess(&ip, &ts);
TEST_CHECK(index >= 0);
CLG_LimitCommandResponseRate(index);
}
- UTI_AddDoubleToTimeval(&tv, (1 << random() % 14) / 100.0, &tv);
+ UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts);
}
}
DEBUG_LOG(0, "records %d", ARR_GetSize(records));
- TEST_CHECK(ARR_GetSize(records) == 128);
+ TEST_CHECK(ARR_GetSize(records) == 64);
for (i = j = 0; i < 10000; i++) {
- tv.tv_sec += 1;
- index = CLG_LogNTPAccess(&ip, &tv);
+ ts.tv_sec += 1;
+ index = CLG_LogNTPAccess(&ip, &ts);
TEST_CHECK(index >= 0);
if (!CLG_LimitNTPResponseRate(index))
j++;
diff --git a/test/unit/hwclock.c b/test/unit/hwclock.c
new file mode 100644
index 0000000..f5667ed
--- /dev/null
+++ b/test/unit/hwclock.c
@@ -0,0 +1,72 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+ */
+
+#include <hwclock.c>
+#include "test.h"
+
+void
+test_unit(void)
+{
+ struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
+ HCL_Instance clock;
+ double freq, jitter, interval, d;
+ int i, j;
+
+ LCL_Initialise();
+
+ clock = HCL_CreateInstance();
+
+ for (i = 0; i < 2000; i++) {
+ UTI_ZeroTimespec(&start_hw_ts);
+ UTI_ZeroTimespec(&start_local_ts);
+ UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts);
+ UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts);
+
+ DEBUG_LOG(0, "iteration %d", i);
+
+ freq = TST_GetRandomDouble(0.9, 1.1);
+ jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
+ interval = TST_GetRandomDouble(MIN_SAMPLE_SEPARATION / 10, MIN_SAMPLE_SEPARATION * 10.0);
+
+ clock->n_samples = 0;
+ clock->valid_coefs = 0;
+
+ for (j = 0; j < 100; j++) {
+ UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
+ UTI_AddDoubleToTimespec(&start_local_ts, j * interval, &local_ts);
+ if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
+ d = UTI_DiffTimespecsToDouble(&ts, &local_ts);
+ TEST_CHECK(fabs(d) <= 5.0 * jitter);
+ }
+
+ if (HCL_NeedsNewSample(clock, &local_ts))
+ HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
+
+ TEST_CHECK(j < 20 || clock->valid_coefs);
+
+ if (!clock->valid_coefs)
+ continue;
+
+ TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
+ }
+ }
+
+ LCL_Finalise();
+}
diff --git a/test/unit/smooth.c b/test/unit/smooth.c
index 153a3e2..0cc6a40 100644
--- a/test/unit/smooth.c
+++ b/test/unit/smooth.c
@@ -25,7 +25,7 @@ void
test_unit(void)
{
int i, j;
- struct timeval tv;
+ struct timespec ts;
double offset, freq, wander;
char conf[] = "smoothtime 300 0.01";
@@ -37,19 +37,19 @@ test_unit(void)
locked = 0;
for (i = 0; i < 500; i++) {
- tv.tv_sec = tv.tv_usec = 0;
- SMT_Reset(&tv);
+ UTI_ZeroTimespec(&ts);
+ SMT_Reset(&ts);
DEBUG_LOG(0, "iteration %d", i);
offset = (random() % 1000000 - 500000) / 1.0e6;
freq = (random() % 1000000 - 500000) / 1.0e9;
- update_smoothing(&tv, offset, freq);
+ update_smoothing(&ts, offset, freq);
for (j = 0; j < 10000; j++) {
- update_smoothing(&tv, 0.0, 0.0);
- UTI_AddDoubleToTimeval(&tv, 16.0, &tv);
- get_smoothing(&tv, &offset, &freq, &wander);
+ update_smoothing(&ts, 0.0, 0.0);
+ UTI_AddDoubleToTimespec(&ts, 16.0, &ts);
+ get_smoothing(&ts, &offset, &freq, &wander);
}
TEST_CHECK(fabs(offset) < 1e-12);
diff --git a/test/unit/sources.c b/test/unit/sources.c
index d748cc2..f166dfd 100644
--- a/test/unit/sources.c
+++ b/test/unit/sources.c
@@ -29,7 +29,7 @@ test_unit(void)
IPAddr addr;
int i, j, k, l, samples, sel_options;
double offset, delay, disp;
- struct timeval tv;
+ struct timespec ts;
CNF_Initialise(0);
LCL_Initialise();
@@ -61,8 +61,8 @@ test_unit(void)
offset = TST_GetRandomDouble(-1.0, 1.0);
for (k = 0; k < samples; k++) {
- SCH_GetLastEventTime(&tv, NULL, NULL);
- UTI_AddDoubleToTimeval(&tv, TST_GetRandomDouble(k - samples, k - samples + 1), &tv);
+ SCH_GetLastEventTime(&ts, NULL, NULL);
+ UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(k - samples, k - samples + 1), &ts);
offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
@@ -71,7 +71,7 @@ test_unit(void)
DEBUG_LOG(0, "source %d sample %d offset %f delay %f disp %f", j, k,
offset, delay, disp);
- SRC_AccumulateSample(srcs[j], &tv, offset, delay, disp, delay, disp,
+ SRC_AccumulateSample(srcs[j], &ts, offset, delay, disp, delay, disp,
1, LEAP_Normal);
}
@@ -124,7 +124,7 @@ test_unit(void)
}
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
- SRC_ReportSource(j, &report, &tv);
+ SRC_ReportSource(j, &report, &ts);
SRC_DestroyInstance(srcs[j]);
}
}
diff --git a/test/unit/test.c b/test/unit/test.c
index d8d20d1..29f23ac 100644
--- a/test/unit/test.c
+++ b/test/unit/test.c
@@ -150,7 +150,7 @@ apply_step_offset(double offset)
}
static void
-offset_convert(struct timeval *raw, double *corr, double *err)
+offset_convert(struct timespec *raw, double *corr, double *err)
{
*corr = 0.0;
if (err)
diff --git a/test/unit/util.c b/test/unit/util.c
new file mode 100644
index 0000000..4637e7e
--- /dev/null
+++ b/test/unit/util.c
@@ -0,0 +1,140 @@
+#include <util.c>
+#include "test.h"
+
+void test_unit(void) {
+ NTP_int64 ntp_ts, ntp_fuzz;
+ struct timespec ts, ts2;
+ double x, y;
+ Float f;
+ int i, j, c;
+ char buf[16];
+
+ for (i = -31; i < 31; i++) {
+ x = pow(2.0, i);
+ y = UTI_Log2ToDouble(i);
+ TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
+ }
+
+ for (i = -89; i < 63; i++) {
+ x = pow(2.0, i);
+ y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
+ TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
+ }
+
+ for (i = 0; i < 100000; i++) {
+ x = TST_GetRandomDouble(-1000.0, 1000.0);
+ y = UTI_FloatNetworkToHost(UTI_FloatHostToNetwork(x));
+ TEST_CHECK(y / x > 0.99999 && y / x < 1.00001);
+
+ UTI_GetRandomBytes(&f, sizeof (f));
+ x = UTI_FloatNetworkToHost(f);
+ TEST_CHECK(x > 0.0 || x <= 0.0);
+ }
+
+ TEST_CHECK(UTI_DoubleToNtp32(1.0) == htonl(65536));
+ TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0));
+ TEST_CHECK(UTI_DoubleToNtp32(1.0 / (65536.0)) == htonl(1));
+ TEST_CHECK(UTI_DoubleToNtp32(1.000001 / (65536.0)) == htonl(2));
+ TEST_CHECK(UTI_DoubleToNtp32(1.000001) == htonl(65537));
+ TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff));
+ TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0));
+
+ ntp_ts.hi = htonl(JAN_1970);
+ ntp_ts.lo = 0xffffffff;
+ UTI_Ntp64ToTimespec(&ntp_ts, &ts);
+ TEST_CHECK(ts.tv_sec == 1);
+ TEST_CHECK(ts.tv_nsec == 0);
+
+ ntp_fuzz.hi = 0;
+ ntp_fuzz.lo = htonl(0xff1234ff);
+
+ UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
+ TEST_CHECK(ntp_ts.hi == htonl(JAN_1970 + 1));
+ TEST_CHECK(ntp_ts.lo == ntp_fuzz.lo);
+
+ ts.tv_sec = ts.tv_nsec = 0;
+ UTI_TimespecToNtp64(&ts, &ntp_ts, &ntp_fuzz);
+ TEST_CHECK(ntp_ts.hi == 0);
+ TEST_CHECK(ntp_ts.lo == 0);
+
+ TEST_CHECK(UTI_IsZeroTimespec(&ts));
+ TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
+
+ ts.tv_sec = 1;
+ ntp_ts.hi = htonl(1);
+
+ TEST_CHECK(!UTI_IsZeroTimespec(&ts));
+ TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
+
+ ts.tv_sec = 0;
+ ntp_ts.hi = 0;
+ ts.tv_nsec = 1;
+ ntp_ts.lo = htonl(1);
+
+ TEST_CHECK(!UTI_IsZeroTimespec(&ts));
+ TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
+
+ ntp_fuzz.hi = htonl(1);
+ ntp_fuzz.lo = htonl(3);
+ ntp_ts.hi = htonl(1);
+ ntp_ts.lo = htonl(2);
+
+ TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
+ TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
+ TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
+
+ ntp_ts.hi = htonl(0x80000002);
+ ntp_ts.lo = htonl(2);
+
+ TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
+ TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
+ TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
+
+ ntp_fuzz.hi = htonl(0x90000001);
+
+ TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_ts) == 0);
+ TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
+ TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 2;
+ ts2.tv_sec = 1;
+ ts2.tv_nsec = 3;
+
+ TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
+ TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
+ TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
+
+ ts2.tv_sec = 2;
+
+ TEST_CHECK(UTI_CompareTimespecs(&ts, &ts) == 0);
+ TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) < 0);
+ TEST_CHECK(UTI_CompareTimespecs(&ts2, &ts) > 0);
+
+ for (i = -32; i <= 32; i++) {
+ for (j = c = 0; j < 1000; j++) {
+ UTI_GetNtp64Fuzz(&ntp_fuzz, i);
+ if (i <= 0)
+ TEST_CHECK(ntp_fuzz.hi == 0);
+ if (i < 0)
+ TEST_CHECK(ntohl(ntp_fuzz.lo) < 1U << (32 + i));
+ else if (i < 32)
+ TEST_CHECK(ntohl(ntp_fuzz.hi) < 1U << i);
+ if (ntohl(ntp_fuzz.lo) >= 1U << (31 + CLAMP(-31, i, 0)))
+ c++;
+ }
+
+ if (i == -32)
+ TEST_CHECK(c == 0);
+ else
+ TEST_CHECK(c > 400 && c < 600);
+ }
+
+ for (i = c = 0; i < 100000; i++) {
+ j = random() % (sizeof (buf) + 1);
+ UTI_GetRandomBytes(buf, j);
+ if (j && buf[j - 1] % 2)
+ c++;
+ }
+ TEST_CHECK(c > 46000 && c < 48000);
+}
diff --git a/util.c b/util.c
index 9085ff6..0488f01 100644
--- a/util.c
+++ b/util.c
@@ -34,13 +34,84 @@
#include "util.h"
#include "hash.h"
+#define NSEC_PER_SEC 1000000000
+
+/* ================================================== */
+
+void
+UTI_ZeroTimespec(struct timespec *ts)
+{
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+}
+
+/* ================================================== */
+
+int
+UTI_IsZeroTimespec(struct timespec *ts)
+{
+ return !ts->tv_sec && !ts->tv_nsec;
+}
+
+/* ================================================== */
+
+void
+UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts)
+{
+ ts->tv_sec = tv->tv_sec;
+ ts->tv_nsec = 1000 * tv->tv_usec;
+}
+
+/* ================================================== */
+
+void
+UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv)
+{
+ tv->tv_sec = ts->tv_sec;
+ tv->tv_usec = ts->tv_nsec / 1000;
+}
+
+/* ================================================== */
+
+double
+UTI_TimespecToDouble(struct timespec *ts)
+{
+ return ts->tv_sec + 1.0e-9 * ts->tv_nsec;
+}
+
+/* ================================================== */
+
+void
+UTI_DoubleToTimespec(double d, struct timespec *ts)
+{
+ ts->tv_sec = d;
+ ts->tv_nsec = 1.0e9 * (d - ts->tv_sec);
+ UTI_NormaliseTimespec(ts);
+}
+
/* ================================================== */
void
-UTI_TimevalToDouble(struct timeval *a, double *b)
+UTI_NormaliseTimespec(struct timespec *ts)
{
- *b = (double)(a->tv_sec) + 1.0e-6 * (double)(a->tv_usec);
+ if (ts->tv_nsec >= NSEC_PER_SEC || ts->tv_nsec < 0) {
+ ts->tv_sec += ts->tv_nsec / NSEC_PER_SEC;
+ ts->tv_nsec = ts->tv_nsec % NSEC_PER_SEC;
+
+ /* If seconds are negative nanoseconds would end up negative too */
+ if (ts->tv_nsec < 0) {
+ ts->tv_sec--;
+ ts->tv_nsec += NSEC_PER_SEC;
+ }
+ }
+}
+/* ================================================== */
+
+double
+UTI_TimevalToDouble(struct timeval *tv)
+{
+ return tv->tv_sec + 1.0e-6 * tv->tv_usec;
}
/* ================================================== */
@@ -60,26 +131,6 @@ UTI_DoubleToTimeval(double a, struct timeval *b)
/* ================================================== */
-int
-UTI_CompareTimevals(struct timeval *a, struct timeval *b)
-{
- if (a->tv_sec < b->tv_sec) {
- return -1;
- } else if (a->tv_sec > b->tv_sec) {
- return +1;
- } else {
- if (a->tv_usec < b->tv_usec) {
- return -1;
- } else if (a->tv_usec > b->tv_usec) {
- return +1;
- } else {
- return 0;
- }
- }
-}
-
-/* ================================================== */
-
void
UTI_NormaliseTimeval(struct timeval *x)
{
@@ -99,100 +150,73 @@ UTI_NormaliseTimeval(struct timeval *x)
/* ================================================== */
-void
-UTI_DiffTimevals(struct timeval *result,
- struct timeval *a,
- struct timeval *b)
+int
+UTI_CompareTimespecs(struct timespec *a, struct timespec *b)
{
- result->tv_sec = a->tv_sec - b->tv_sec;
- result->tv_usec = a->tv_usec - b->tv_usec;
+ if (a->tv_sec < b->tv_sec)
+ return -1;
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+ if (a->tv_nsec < b->tv_nsec)
+ return -1;
+ if (a->tv_nsec > b->tv_nsec)
+ return 1;
+ return 0;
+}
- /* Correct microseconds field to bring it into the range
- (0,1000000) */
+/* ================================================== */
- UTI_NormaliseTimeval(result); /* JGH */
+void
+UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b)
+{
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ result->tv_nsec = a->tv_nsec - b->tv_nsec;
+ UTI_NormaliseTimespec(result);
}
/* ================================================== */
/* Calculate result = a - b and return as a double */
-void
-UTI_DiffTimevalsToDouble(double *result,
- struct timeval *a,
- struct timeval *b)
+double
+UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b)
{
- *result = (double)(a->tv_sec - b->tv_sec) +
- (double)(a->tv_usec - b->tv_usec) * 1.0e-6;
+ return (a->tv_sec - b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
}
/* ================================================== */
void
-UTI_AddDoubleToTimeval(struct timeval *start,
- double increment,
- struct timeval *end)
+UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end)
{
- long int_part, frac_part;
+ time_t int_part;
- /* Don't want to do this by using (long)(1000000 * increment), since
- that will only cope with increments up to +/- 2148 seconds, which
- is too marginal here. */
-
- int_part = (long) increment;
- increment = (increment - int_part) * 1.0e6;
- frac_part = (long) (increment > 0.0 ? increment + 0.5 : increment - 0.5);
-
- end->tv_sec = int_part + start->tv_sec;
- end->tv_usec = frac_part + start->tv_usec;
-
- UTI_NormaliseTimeval(end);
+ int_part = increment;
+ end->tv_sec = start->tv_sec + int_part;
+ end->tv_nsec = start->tv_nsec + 1.0e9 * (increment - int_part);
+ UTI_NormaliseTimespec(end);
}
/* ================================================== */
-/* Calculate the average and difference (as a double) of two timevals */
+/* Calculate the average and difference (as a double) of two timespecs */
void
-UTI_AverageDiffTimevals (struct timeval *earlier,
- struct timeval *later,
- struct timeval *average,
- double *diff)
+UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later,
+ struct timespec *average, double *diff)
{
- struct timeval tvdiff;
- struct timeval tvhalf;
-
- UTI_DiffTimevals(&tvdiff, later, earlier);
- *diff = (double)tvdiff.tv_sec + 1.0e-6 * (double)tvdiff.tv_usec;
-
- if (*diff < 0.0) {
- /* Either there's a bug elsewhere causing 'earlier' and 'later' to
- be backwards, or something wierd has happened. Maybe when we
- change the frequency on Linux? */
-
- /* Assume the required behaviour is to treat it as zero */
- *diff = 0.0;
- }
-
- tvhalf.tv_sec = tvdiff.tv_sec / 2;
- tvhalf.tv_usec = tvdiff.tv_usec / 2 + (tvdiff.tv_sec % 2) * 500000; /* JGH */
-
- average->tv_sec = earlier->tv_sec + tvhalf.tv_sec;
- average->tv_usec = earlier->tv_usec + tvhalf.tv_usec;
-
- /* Bring into range */
- UTI_NormaliseTimeval(average);
-
- }
+ *diff = UTI_DiffTimespecsToDouble(later, earlier);
+ UTI_AddDoubleToTimespec(earlier, *diff / 2.0, average);
+}
/* ================================================== */
void
-UTI_AddDiffToTimeval(struct timeval *a, struct timeval *b,
- struct timeval *c, struct timeval *result)
+UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b,
+ struct timespec *c, struct timespec *result)
{
double diff;
- UTI_DiffTimevalsToDouble(&diff, a, b);
- UTI_AddDoubleToTimeval(c, diff, result);
+ diff = UTI_DiffTimespecsToDouble(a, b);
+ UTI_AddDoubleToTimespec(c, diff, result);
}
/* ================================================== */
@@ -205,21 +229,20 @@ static int pool_ptr = 0;
#define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)])
/* ================================================== */
-/* Convert a timeval into a temporary string, largely for diagnostic
- display */
+/* Convert a timespec into a temporary string, largely for diagnostic display */
char *
-UTI_TimevalToString(struct timeval *tv)
+UTI_TimespecToString(struct timespec *ts)
{
char *result;
result = NEXT_BUFFER;
#ifdef HAVE_LONG_TIME_T
- snprintf(result, BUFFER_LENGTH, "%"PRId64".%06lu",
- (int64_t)tv->tv_sec, (unsigned long)tv->tv_usec);
+ snprintf(result, BUFFER_LENGTH, "%"PRId64".%09lu",
+ (int64_t)ts->tv_sec, (unsigned long)ts->tv_nsec);
#else
- snprintf(result, BUFFER_LENGTH, "%ld.%06lu",
- (long)tv->tv_sec, (unsigned long)tv->tv_usec);
+ snprintf(result, BUFFER_LENGTH, "%ld.%09lu",
+ (long)ts->tv_sec, (unsigned long)ts->tv_nsec);
#endif
return result;
}
@@ -229,11 +252,11 @@ UTI_TimevalToString(struct timeval *tv)
for diagnostic display */
char *
-UTI_TimestampToString(NTP_int64 *ts)
+UTI_Ntp64ToString(NTP_int64 *ntp_ts)
{
- struct timeval tv;
- UTI_Int64ToTimeval(ts, &tv);
- return UTI_TimevalToString(&tv);
+ struct timespec ts;
+ UTI_Ntp64ToTimespec(ntp_ts, &ts);
+ return UTI_TimespecToString(&ts);
}
/* ================================================== */
@@ -408,6 +431,8 @@ UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest)
case IPADDR_INET6:
memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
break;
+ default:
+ dest->family = htons(IPADDR_UNSPEC);
}
}
@@ -425,6 +450,8 @@ UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest)
case IPADDR_INET6:
memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
break;
+ default:
+ dest->family = IPADDR_UNSPEC;
}
}
@@ -589,19 +616,19 @@ UTI_TimeToLogForm(time_t t)
/* ================================================== */
void
-UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double *delta_time, double dfreq, double doffset)
+UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
{
double elapsed;
- UTI_DiffTimevalsToDouble(&elapsed, when, old_tv);
+ elapsed = UTI_DiffTimespecsToDouble(when, old_ts);
*delta_time = elapsed * dfreq - doffset;
- UTI_AddDoubleToTimeval(old_tv, *delta_time, new_tv);
+ UTI_AddDoubleToTimespec(old_ts, *delta_time, new_ts);
}
/* ================================================== */
void
-UTI_GetInt64Fuzz(NTP_int64 *ts, int precision)
+UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision)
{
int start, bits;
@@ -620,7 +647,7 @@ UTI_GetInt64Fuzz(NTP_int64 *ts, int precision)
/* ================================================== */
double
-UTI_Int32ToDouble(NTP_int32 x)
+UTI_Ntp32ToDouble(NTP_int32 x)
{
return (double) ntohl(x) / 65536.0;
}
@@ -630,39 +657,84 @@ UTI_Int32ToDouble(NTP_int32 x)
#define MAX_NTP_INT32 (4294967295.0 / 65536.0)
NTP_int32
-UTI_DoubleToInt32(double x)
+UTI_DoubleToNtp32(double x)
{
- if (x > MAX_NTP_INT32)
- x = MAX_NTP_INT32;
- else if (x < 0)
- x = 0.0;
- return htonl((NTP_int32)(0.5 + 65536.0 * x));
+ NTP_int32 r;
+
+ if (x >= MAX_NTP_INT32) {
+ r = 0xffffffff;
+ } else if (x <= 0.0) {
+ r = 0;
+ } else {
+ x *= 65536.0;
+ r = x;
+
+ /* Round up */
+ if (r < x)
+ r++;
+ }
+
+ return htonl(r);
+}
+
+/* ================================================== */
+
+void
+UTI_ZeroNtp64(NTP_int64 *ts)
+{
+ ts->hi = ts->lo = htonl(0);
+}
+
+/* ================================================== */
+
+int
+UTI_IsZeroNtp64(NTP_int64 *ts)
+{
+ return !ts->hi && !ts->lo;
+}
+
+/* ================================================== */
+
+int
+UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
+{
+ int32_t diff;
+
+ if (a->hi == b->hi && a->lo == b->lo)
+ return 0;
+
+ diff = ntohl(a->hi) - ntohl(b->hi);
+
+ if (diff < 0)
+ return -1;
+ if (diff > 0)
+ return 1;
+
+ return ntohl(a->lo) < ntohl(b->lo) ? -1 : 1;
}
/* ================================================== */
-/* Seconds part of NTP timestamp correponding to the origin of the
- struct timeval format. */
+/* Seconds part of NTP timestamp correponding to the origin of the time_t format */
#define JAN_1970 0x83aa7e80UL
+#define NSEC_PER_NTP64 4.294967296
+
void
-UTI_TimevalToInt64(struct timeval *src,
- NTP_int64 *dest, NTP_int64 *fuzz)
+UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz)
{
- uint32_t hi, lo, sec, usec;
+ uint32_t hi, lo, sec, nsec;
sec = (uint32_t)src->tv_sec;
- usec = (uint32_t)src->tv_usec;
+ nsec = (uint32_t)src->tv_nsec;
/* Recognize zero as a special case - it always signifies
an 'unknown' value */
- if (!usec && !sec) {
+ if (!nsec && !sec) {
hi = lo = 0;
} else {
hi = htonl(sec + JAN_1970);
-
- /* This formula gives an error of about 0.1us worst case */
- lo = htonl(4295 * usec - (usec >> 5) - (usec >> 9));
+ lo = htonl(NSEC_PER_NTP64 * nsec);
/* Add the fuzz */
if (fuzz) {
@@ -678,8 +750,7 @@ UTI_TimevalToInt64(struct timeval *src,
/* ================================================== */
void
-UTI_Int64ToTimeval(NTP_int64 *src,
- struct timeval *dest)
+UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
{
uint32_t ntp_sec, ntp_frac;
@@ -695,9 +766,10 @@ UTI_Int64ToTimeval(NTP_int64 *src,
#else
dest->tv_sec = ntp_sec - JAN_1970;
#endif
-
- /* Until I invent a slick way to do this, just do it the obvious way */
- dest->tv_usec = (int)(0.5 + (double)(ntp_frac) / 4294.967296);
+
+ dest->tv_nsec = ntp_frac / NSEC_PER_NTP64 + 0.5;
+
+ UTI_NormaliseTimespec(dest);
}
/* ================================================== */
@@ -709,7 +781,7 @@ UTI_Int64ToTimeval(NTP_int64 *src,
#define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600)
int
-UTI_IsTimeOffsetSane(struct timeval *tv, double offset)
+UTI_IsTimeOffsetSane(struct timespec *ts, double offset)
{
double t;
@@ -717,8 +789,7 @@ UTI_IsTimeOffsetSane(struct timeval *tv, double offset)
if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET))
return 0;
- UTI_TimevalToDouble(tv, &t);
- t += offset;
+ t = UTI_TimespecToDouble(ts) + offset;
/* Time before 1970 is not considered valid */
if (t < 0.0)
@@ -756,14 +827,14 @@ UTI_Log2ToDouble(int l)
/* ================================================== */
void
-UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest)
+UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
{
uint32_t sec_low;
#ifdef HAVE_LONG_TIME_T
uint32_t sec_high;
#endif
- dest->tv_usec = ntohl(src->tv_nsec) / 1000;
+ dest->tv_nsec = ntohl(src->tv_nsec);
sec_low = ntohl(src->tv_sec_low);
#ifdef HAVE_LONG_TIME_T
sec_high = ntohl(src->tv_sec_high);
@@ -774,14 +845,16 @@ UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest)
#else
dest->tv_sec = sec_low;
#endif
+
+ UTI_NormaliseTimespec(dest);
}
/* ================================================== */
void
-UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest)
+UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest)
{
- dest->tv_nsec = htonl(src->tv_usec * 1000);
+ dest->tv_nsec = htonl(src->tv_nsec);
#ifdef HAVE_LONG_TIME_T
dest->tv_sec_high = htonl((uint64_t)src->tv_sec >> 32);
#else
@@ -895,57 +968,6 @@ UTI_FdSetCloexec(int fd)
/* ================================================== */
int
-UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len,
- const unsigned char *data, int data_len, unsigned char *auth, int auth_len)
-{
- return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
-}
-
-/* ================================================== */
-
-int
-UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len,
- const unsigned char *data, int data_len, const unsigned char *auth, int auth_len)
-{
- unsigned char buf[MAX_HASH_LENGTH];
-
- return UTI_GenerateNTPAuth(hash_id, key, key_len, data, data_len,
- buf, sizeof (buf)) == auth_len && !memcmp(buf, auth, auth_len);
-}
-
-/* ================================================== */
-
-int
-UTI_DecodePasswordFromText(char *key)
-{
- int i, j, len = strlen(key);
- char buf[3], *p;
-
- if (!strncmp(key, "ASCII:", 6)) {
- memmove(key, key + 6, len - 6);
- return len - 6;
- } else if (!strncmp(key, "HEX:", 4)) {
- if ((len - 4) % 2)
- return 0;
-
- for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
- buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
- key[i] = strtol(buf, &p, 16);
-
- if (p != buf + 2)
- return 0;
- }
-
- return i;
- } else {
- /* assume ASCII */
- return len;
- }
-}
-
-/* ================================================== */
-
-int
UTI_SetQuitSignalsHandler(void (*handler)(int))
{
struct sigaction sa;
diff --git a/util.h b/util.h
index a019fe5..02d598b 100644
--- a/util.h
+++ b/util.h
@@ -34,45 +34,68 @@
#include "candm.h"
#include "hash.h"
+/* Zero a timespec */
+extern void UTI_ZeroTimespec(struct timespec *ts);
+
+/* Check if a timespec is zero */
+extern int UTI_IsZeroTimespec(struct timespec *ts);
+
+/* Convert a timeval into a timespec */
+extern void UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts);
+
+/* Convert a timespec into a timeval */
+extern void UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv);
+
+/* Convert a timespec into a floating point number of seconds */
+extern double UTI_TimespecToDouble(struct timespec *ts);
+
+/* Convert a number of seconds expressed in floating point into a
+ timespec */
+extern void UTI_DoubleToTimespec(double d, struct timespec *ts);
+
+/* Normalise a timespec, by adding or subtracting seconds to bring
+ its nanosecond field into range */
+extern void UTI_NormaliseTimespec(struct timespec *ts);
+
/* Convert a timeval into a floating point number of seconds */
-extern void UTI_TimevalToDouble(struct timeval *a, double *b);
+extern double UTI_TimevalToDouble(struct timeval *tv);
/* Convert a number of seconds expressed in floating point into a
timeval */
extern void UTI_DoubleToTimeval(double a, struct timeval *b);
-/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
- and +1 if a comes after b */
-extern int UTI_CompareTimevals(struct timeval *a, struct timeval *b);
-
/* Normalise a struct timeval, by adding or subtracting seconds to bring
its microseconds field into range */
extern void UTI_NormaliseTimeval(struct timeval *x);
+/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
+ and +1 if a comes after b */
+extern int UTI_CompareTimespecs(struct timespec *a, struct timespec *b);
+
/* Calculate result = a - b */
-extern void UTI_DiffTimevals(struct timeval *result, struct timeval *a, struct timeval *b);
+extern void UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b);
/* Calculate result = a - b and return as a double */
-extern void UTI_DiffTimevalsToDouble(double *result, struct timeval *a, struct timeval *b);
+extern double UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b);
-/* Add a double increment to a timeval to get a new one. 'start' is
+/* Add a double increment to a timespec to get a new one. 'start' is
the starting time, 'end' is the result that we return. This is
safe to use if start and end are the same */
-extern void UTI_AddDoubleToTimeval(struct timeval *start, double increment, struct timeval *end);
+extern void UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end);
-/* Calculate the average and difference (as a double) of two timevals */
-extern void UTI_AverageDiffTimevals(struct timeval *earlier, struct timeval *later, struct timeval *average, double *diff);
+/* Calculate the average and difference (as a double) of two timespecs */
+extern void UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later, struct timespec *average, double *diff);
/* Calculate result = a - b + c */
-extern void UTI_AddDiffToTimeval(struct timeval *a, struct timeval *b, struct timeval *c, struct timeval *result);
+extern void UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b, struct timespec *c, struct timespec *result);
-/* Convert a timeval into a temporary string, largely for diagnostic
+/* Convert a timespec into a temporary string, largely for diagnostic
display */
-extern char *UTI_TimevalToString(struct timeval *tv);
+extern char *UTI_TimespecToString(struct timespec *ts);
/* Convert an NTP timestamp into a temporary string, largely for
diagnostic display */
-extern char *UTI_TimestampToString(NTP_int64 *ts);
+extern char *UTI_Ntp64ToString(NTP_int64 *ts);
/* Convert ref_id into a temporary string, for diagnostics */
extern char *UTI_RefidToString(uint32_t ref_id);
@@ -95,26 +118,38 @@ extern const char *UTI_SockaddrFamilyToString(int family);
extern char *UTI_TimeToLogForm(time_t t);
/* Adjust time following a frequency/offset change */
-extern void UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double *delta, double dfreq, double doffset);
+extern void UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset);
/* Get zero NTP timestamp with random bits below precision */
-extern void UTI_GetInt64Fuzz(NTP_int64 *ts, int precision);
+extern void UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision);
+
+extern double UTI_Ntp32ToDouble(NTP_int32 x);
+extern NTP_int32 UTI_DoubleToNtp32(double x);
-extern double UTI_Int32ToDouble(NTP_int32 x);
-extern NTP_int32 UTI_DoubleToInt32(double x);
+/* Zero an NTP timestamp */
+extern void UTI_ZeroNtp64(NTP_int64 *ts);
-extern void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest, NTP_int64 *fuzz);
+/* Check if an NTP timestamp is zero */
+extern int UTI_IsZeroNtp64(NTP_int64 *ts);
-extern void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest);
+/* Compare two NTP timestamps. Returns -1 if a is before b, 0 if a is equal to
+ b, and 1 if a is after b. */
+extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b);
+
+/* Convert a timespec into an NTP timestamp */
+extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz);
+
+/* Convert an NTP timestamp into a timespec */
+extern void UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest);
/* Check if time + offset is sane */
-extern int UTI_IsTimeOffsetSane(struct timeval *tv, double offset);
+extern int UTI_IsTimeOffsetSane(struct timespec *ts, double offset);
/* Get 2 raised to power of a signed integer */
extern double UTI_Log2ToDouble(int l);
-extern void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest);
-extern void UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest);
+extern void UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest);
+extern void UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest);
extern double UTI_FloatNetworkToHost(Float x);
extern Float UTI_FloatHostToNetwork(double x);
@@ -122,14 +157,6 @@ extern Float UTI_FloatHostToNetwork(double x);
/* Set FD_CLOEXEC on descriptor */
extern int UTI_FdSetCloexec(int fd);
-extern int UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len,
- const unsigned char *data, int data_len, unsigned char *auth, int auth_len);
-extern int UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len,
- const unsigned char *data, int data_len, const unsigned char *auth, int auth_len);
-
-/* Decode password encoded in ASCII or HEX */
-extern int UTI_DecodePasswordFromText(char *key);
-
extern int UTI_SetQuitSignalsHandler(void (*handler)(int));
/* Get directory (as an allocated string) for a path */
diff --git a/version.txt b/version.txt
index 005119b..34c26e0 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-2.4.1
+3.0-pre1