Compare commits

..

43 Commits

Author SHA1 Message Date
Miroslav Lichvar
d5c507975c doc: update README 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
b4235abd36 update copyright years 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
1966085a97 test: add ntp_core unit test 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
e31e7af48f test: make 119-smoothtime more reliable 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
adb9123fc3 test: extend util unit test 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
b0f7efd59e util: handle zero in conversion of NTP timestamps
Handle zero NTP timestamp in UTI_Ntp64ToTimespec() as a special value to
make it symmetric with UTI_TimespecToNtp64(). This is needed since
commit d75f6830f1, in which a timestamp is
converted back and forth without checking for zero.

It also makes zero NTP timestamps more apparent in debug output.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
e28dfada8c rtc: check for backward RTC steps
When accumulating a new sample, check if the new RTC time is newer the
last sample time. If it is not, discard all previous samples, assuming
something has stepped the RTC, or it's a broken RTC/driver.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
ac0b28cce6 sourcestats: align sample time used for source report
This reduces leak of sample times (and receive timestamps which are
related to sample times), which could be useful in off-path attacks on
unauthenticated symmetric interleaved mode.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
48b16ae66c local: add assertion for precision 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
061579ec28 ntp: don't send packets with RX equal to TX
Before sending an NTP packet, check whether the TX timestamp is not
equal to the RX timestamp. If it is, generate a new TX timestamp and try
again. This is extremely unlikely to happen in normal operation, but it
is needed for reliable detection of the interleaved mode.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
f2f834e7e7 ntp: limit maxdelay parameters 2017-01-12 16:34:27 +01:00
Miroslav Lichvar
a7802e9a76 fix some coverity warnings 2017-01-12 16:34:27 +01:00
Miroslav Lichvar
8f7ab95ff0 doc: update NEWS 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
042c670747 doc: improve chrony.conf man page 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
cacbe9976f ntp: add options for compensating HW timestamping errors 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
8efec1d640 ntp: add sanity check for HW timestamps
Accept HW timestamp only if it doesn't differ from the kernel/daemon
timestamp by more than one second.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
c44d282f0b ntp: ignore zero HW timestamps
Apparently, zero HW timestamps are possible with buggy drivers/HW.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
4432f29bd2 sources: try to replace jittery sources
Similarly to falsetickers, distant, and unreachable sources, try to
replace sources that have jitter larger than maxjitter.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
5fee3ed5e9 client: print refid also as string in ntpdata output 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
b76ea64263 ntp: log warning when KoD RATE is received in non-burst mode 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
ed904f08a4 hwclock: return timestamp error
For now, when converting a raw timestamp, return error of the last
sample as the maximum error of the timestamp. This is needed to include
the PHC reading delay in the NTP dispersion.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
96cc80ffc8 ntp: improve dispersion calculation
Instead of adding precision (sum of the local and remote precision) to
the TX and RX timestamp error, include only the maximum.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
ab99373cfc conf: change default rate limiting parameters
Change the default NTP rate limiting leak to 2 (25%). Change the default
command rate limiting interval to -4 (16 packets per second) and burst
to 8, so the interval is the only difference between NTP and command
rate limiting defaults.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
dbfb49384b clientlog: disable NTP response rate limiting by default
This reverts commit 50022e9286.

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

Rework the code to use pipes instead of timeouts to avoid this problem.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
b7bd7469b7 ntp: disable maxdelayratio in interleaved/symmetric mode
It's too unreliable and the maxdelaydevratio test should work better
anyway.
2016-12-13 12:57:24 +01:00
29 changed files with 844 additions and 241 deletions

6
NEWS
View File

@@ -16,14 +16,20 @@ Enhancements
* 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
* Allow rate limiting with very short intervals
* Improve maximum server throughput on Linux and NetBSD
* Remove dump files after start
* Add tab-completion to chronyc with libedit/readline
* Add ntpdata command to print details about NTP measurements
* Allow all source options to be set in add server/peer command
* Indicate truncated addresses/hostnames in chronyc output
* Print reference IDs as hexadecimal numbers to avoid confusion with
IPv4 addresses
Bug fixes
---------
* Fix crash with disabled asynchronous name resolving
New in version 2.4.1
====================

5
README
View File

@@ -16,7 +16,7 @@ and systems that do not run continuosly, or run on a virtual machine.
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
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
@@ -203,6 +203,9 @@ Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc

View File

@@ -362,8 +362,8 @@ typedef struct {
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types), new flags in NTP source request and report,
new commands: refresh, serverstats
(using new request/reply types), new fields and flags in NTP source request
and report, new commands: ntpdata, refresh, serverstats
*/
#define PROTO_VERSION_NUMBER 6
@@ -672,6 +672,7 @@ typedef struct {
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;

167
client.c
View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016
* Copyright (C) Miroslav Lichvar 2009-2016
*
* This program is free software; you can redistribute it and/or modify
@@ -1191,7 +1192,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"
"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"
@@ -2244,71 +2245,107 @@ process_cmd_ntpdata(char *line)
CMD_Reply reply;
IPAddr remote_addr, local_addr;
struct timespec ref_time;
uint32_t i, n_sources;
uint16_t mode;
int specified_addr;
if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
return 0;
if (*line) {
specified_addr = 1;
n_sources = 1;
} else {
specified_addr = 0;
request.command = htons(REQ_N_SOURCES);
if (!request_reply(&request, &reply, RPY_N_SOURCES, 0))
return 0;
n_sources = ntohl(reply.data.n_sources.n_sources);
}
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;
for (i = 0; i < n_sources; i++) {
if (specified_addr) {
if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
return 0;
}
} else {
request.command = htons(REQ_SOURCE_DATA);
request.data.source_data.index = htonl(i);
if (!request_reply(&request, &reply, RPY_SOURCE_DATA, 0))
return 0;
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);
mode = ntohs(reply.data.source_data.mode);
if (mode != RPY_SD_MD_CLIENT && mode != RPY_SD_MD_PEER)
continue;
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);
UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &remote_addr);
}
request.command = htons(REQ_NTP_DATA);
UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
return 0;
UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
UTI_IPNetworkToHost(&reply.data.ntp_data.local_addr, &local_addr);
UTI_TimespecNetworkToHost(&reply.data.ntp_data.ref_time, &ref_time);
if (!specified_addr && !csv_mode)
printf("\n");
print_report("Remote address : %s (%R)\n"
"Remote port : %u\n"
"Local address : %s (%R)\n"
"Leap status : %L\n"
"Version : %u\n"
"Mode : %M\n"
"Stratum : %u\n"
"Poll interval : %d (%.0f seconds)\n"
"Precision : %d (%.9f seconds)\n"
"Root delay : %.6f seconds\n"
"Root dispersion : %.6f seconds\n"
"Reference ID : %R (%s)\n"
"Reference time : %T\n"
"Offset : %+.9f seconds\n"
"Peer delay : %.9f seconds\n"
"Peer dispersion : %.9f seconds\n"
"Response time : %.9f seconds\n"
"Jitter asymmetry: %+.2f\n"
"NTP tests : %.3b %.3b %.4b\n"
"Interleaved : %B\n"
"Authenticated : %B\n"
"TX timestamping : %N\n"
"RX timestamping : %N\n"
"Total TX : %U\n"
"Total RX : %U\n"
"Total valid RX : %U\n",
UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port),
UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
reply.data.ntp_data.leap, reply.data.ntp_data.version,
reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
(unsigned long)ntohl(reply.data.ntp_data.ref_id),
reply.data.ntp_data.stratum <= 1 ?
UTI_RefidToString(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;
}
@@ -2687,7 +2724,8 @@ process_cmd_waitsync(char *line)
max_skew_ppm = 0.0;
interval = 10.0;
sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval);
if (sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval))
;
/* Don't allow shorter interval than 0.1 seconds */
if (interval < 0.1)
@@ -2795,7 +2833,8 @@ process_cmd_keygen(char *line)
snprintf(hash_name, sizeof (hash_name), "MD5");
#endif
sscanf(line, "%u %16s %d", &id, hash_name, &bits);
if (sscanf(line, "%u %16s %d", &id, hash_name, &bits))
;
length = CLAMP(10, (bits + 7) / 8, sizeof (key));
if (HSH_GetHashId(hash_name) < 0) {
@@ -3058,7 +3097,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2016 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2017 Richard P. Curnow and others\n"
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n",

View File

@@ -86,6 +86,10 @@ static unsigned int max_slots;
#define TS_FRAC 4
#define INVALID_TS 0
/* Static offset included in conversion to the fixed-point timestamps to
randomise their alignment */
static uint32_t ts_offset;
/* Request rates are saved in the record as 8-bit scaled log2 values */
#define RATE_SCALE 4
#define MIN_RATE (-14 * RATE_SCALE)
@@ -95,7 +99,7 @@ static unsigned int max_slots;
number of tokens spent on response are determined from configured
minimum inverval between responses (in log2) and burst length. */
#define MIN_LIMIT_INTERVAL (-TS_FRAC)
#define MIN_LIMIT_INTERVAL (-15 - TS_FRAC)
#define MAX_LIMIT_INTERVAL 12
#define MIN_LIMIT_BURST 1
#define MAX_LIMIT_BURST 255
@@ -105,7 +109,8 @@ static uint16_t max_cmd_tokens;
static uint16_t ntp_tokens_per_packet;
static uint16_t cmd_tokens_per_packet;
/* Reduction of token rates to avoid overflow of 16-bit counters */
/* Reduction of token rates to avoid overflow of 16-bit counters. Negative
shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
static int ntp_token_shift;
static int cmd_token_shift;
@@ -133,6 +138,8 @@ static uint32_t total_ntp_drops;
static uint32_t total_cmd_drops;
static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U
/* ================================================== */
static int expand_hashtable(void);
@@ -271,10 +278,17 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST);
/* Find smallest shift with which the maximum number fits in 16 bits */
for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
break;
if (interval >= -TS_FRAC) {
/* Find the smallest shift with which the maximum number fits in 16 bits */
for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
break;
}
} else {
/* Coarse rate limiting */
*token_shift = interval + TS_FRAC;
*tokens_per_packet = 1;
burst = MAX(1U << -*token_shift, burst);
}
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
@@ -325,6 +339,9 @@ CLG_Initialise(void)
records = NULL;
expand_hashtable();
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
}
/* ================================================== */
@@ -345,6 +362,12 @@ get_ts_from_timespec(struct timespec *ts)
{
uint32_t sec = ts->tv_sec, nsec = ts->tv_nsec;
nsec += ts_offset;
if (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
sec++;
}
/* This is fast and accurate enough */
return sec << TS_FRAC | (140740U * (nsec >> 15)) >> (32 - TS_FRAC);
}
@@ -369,7 +392,12 @@ update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
if (prev_hit == INVALID_TS || (int32_t)interval < 0)
return;
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift);
if (token_shift >= 0)
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift);
else if (now_ts - prev_hit > max_tokens)
new_tokens = max_tokens;
else
new_tokens = (now_ts - prev_hit) << -token_shift;
*tokens = MIN(*tokens + new_tokens, max_tokens);
/* Convert the interval to scaled and rounded log2 */

View File

@@ -1232,6 +1232,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
/* ================================================== */

71
conf.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016
* Copyright (C) Miroslav Lichvar 2009-2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -194,10 +194,10 @@ static char *pidfile;
static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 3;
static int ntp_ratelimit_leak = 2;
static int cmd_ratelimit_enabled = 0;
static int cmd_ratelimit_interval = 1;
static int cmd_ratelimit_burst = 16;
static int cmd_ratelimit_interval = -4;
static int cmd_ratelimit_burst = 8;
static int cmd_ratelimit_leak = 2;
/* Smoothing constants */
@@ -223,7 +223,13 @@ 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 */
typedef struct {
char *name;
double tx_comp;
double rx_comp;
} HwTs_Interface;
/* Array of HwTs_Interface */
static ARR_Instance hwts_interfaces;
typedef struct {
@@ -327,7 +333,7 @@ CNF_Initialise(int r)
{
restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (char *));
hwts_interfaces = ARR_CreateInstance(sizeof (HwTs_Interface));
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
@@ -354,7 +360,7 @@ CNF_Finalise(void)
unsigned int i;
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
Free(*(char **)ARR_GetElement(hwts_interfaces, i));
Free(((HwTs_Interface *)ARR_GetElement(hwts_interfaces, i))->name);
ARR_DestroyInstance(hwts_interfaces);
for (i = 0; i < ARR_GetSize(ntp_sources); i++)
@@ -1245,8 +1251,39 @@ parse_tempcomp(char *line)
static void
parse_hwtimestamp(char *line)
{
check_number_of_args(line, 1);
*(char **)ARR_GetNewElement(hwts_interfaces) = Strdup(line);
HwTs_Interface *iface;
char *p;
int n;
if (!*line) {
command_parse_error();
return;
}
p = line;
line = CPS_SplitWord(line);
iface = ARR_GetNewElement(hwts_interfaces);
iface->name = Strdup(p);
iface->tx_comp = 0.0;
iface->rx_comp = 0.0;
for (p = line; *p; line += n, p = line) {
line = CPS_SplitWord(line);
if (!strcasecmp(p, "rxcomp")) {
if (sscanf(line, "%lf%n", &iface->rx_comp, &n) != 1)
break;
} else if (!strcasecmp(p, "txcomp")) {
if (sscanf(line, "%lf%n", &iface->tx_comp, &n) != 1)
break;
} else {
break;
}
}
if (*p)
command_parse_error();
}
/* ================================================== */
@@ -1930,8 +1967,18 @@ CNF_GetInitStepThreshold(void)
/* ================================================== */
ARR_Instance
CNF_GetHwTsInterfaces(void)
int
CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp)
{
return hwts_interfaces;
HwTs_Interface *iface;
if (index >= ARR_GetSize(hwts_interfaces))
return 0;
iface = ARR_GetElement(hwts_interfaces, index);
*name = iface->name;
*tx_comp = iface->tx_comp;
*rx_comp = iface->rx_comp;
return 1;
}

3
conf.h
View File

@@ -29,7 +29,6 @@
#define GOT_CONF_H
#include "addressing.h"
#include "array.h"
#include "reference.h"
extern void CNF_Initialise(int restarted);
@@ -120,6 +119,6 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
extern ARR_Instance CNF_GetHwTsInterfaces(void);
extern int CNF_GetHwTsInterface(unsigned int index, char **name, double *tx_comp, double *rx_comp);
#endif /* GOT_CONF_H */

23
configure vendored
View File

@@ -4,7 +4,8 @@
# chronyd/chronyc - Programs for keeping computer clocks accurate.
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Miroslav Lichvar 2009, 2012-2015
# Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2016
#
# =======================================================================
@@ -219,6 +220,7 @@ try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_clock_gettime=1
try_recvmmsg=1
feat_timestamping=1
try_timestamping=0
feat_ntp_signd=0
@@ -400,6 +402,9 @@ case $OPERATINGSYSTEM in
echo "Configuring for " $SYSTEM
;;
FreeBSD)
# recvmmsg() seems to be broken on FreeBSD 11.0 and it's just
# a wrapper around recvmsg()
try_recvmmsg=0
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
add_def FREEBSD
if [ $feat_droproot = "1" ]; then
@@ -629,14 +634,16 @@ 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
if [ $try_recvmmsg = "1" ]; then
if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
add_def HAVE_RECVMMSG
else
if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
"$EXTRA_LIBS" "$RECVMMSG_CODE"
then
add_def _GNU_SOURCE
add_def HAVE_RECVMMSG
fi
fi
fi

View File

@@ -1,7 +1,8 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2016
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
@@ -105,12 +106,13 @@ If the user knows that round trip delays above a certain level should cause the
measurement to be ignored, this level can be defined with the *maxdelay*
option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is
3 seconds.
3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_:::
This option is similar to the maxdelay option above. *chronyd* keeps a record
of the minimum round-trip delay amongst the previous measurements that it has
buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected.
maxdelayratio times the minimum delay, it will be rejected. This option works
only in the *server* directive when not in the interleaved mode.
*maxdelaydevratio* _ratio_:::
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
@@ -159,9 +161,8 @@ 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.
server or the peer is running *chronyd* with software (kernel) or hardware
timestamping). 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,
@@ -993,7 +994,7 @@ both a client of its servers, and a server to other clients.
Examples of the use of the directive are as follows:
+
----
allow foo.example.net
allow 1.2.3.4
allow 1.2
allow 3.4.5
allow 6.7.8/22
@@ -1004,7 +1005,8 @@ allow ::/0
allow
----
+
The first directive allows the named node to be an NTP client of this computer.
The first directive allows a node with IPv4 address _1.2.3.4_ to be an NTP
client of this computer.
The second directive allows any node with an IPv4 address of the form _1.2.x.y_
(with _x_ and _y_ arbitrary) to be an NTP client of this computer. Likewise,
the third directive allows any node with an IPv4 address of the form _3.4.5.x_
@@ -1045,6 +1047,10 @@ Within a configuration file this capability is probably rather moot; however,
it is of greater use for reconfiguration at run-time via *chronyc* with the
<<chronyc.adoc#allow,*allow all*>> command.
+
The directive allows a hostname to be specified instead of an IP address, but
the name must be resolvable when *chronyd* is started (i.e. *chronyd* needs
to be started when the network is already up and DNS is working).
+
Note, if the <<initstepslew,*initstepslew*>> directive is used in the
configuration file, each of the computers listed in that directive must allow
client access by this computer for it to work.
@@ -1237,7 +1243,10 @@ in any order):
*interval*:::
This option sets the minimum interval between responses. It is defined as a
power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
is -4 and the maximum value is 12.
is -19 (524288 packets per second) and the maximum value is 12 (one packet per
4096 seconds). Note that with values below -4 the rate limiting is coarse
(responses are allowed in bursts, even if the interval between them is shorter
than the specified interval).
*burst*:::
This option sets the maximum number of responses that can be sent in a burst,
temporarily exceeding the limit specified by the *interval* option. This is
@@ -1249,20 +1258,20 @@ This option sets the rate at which responses are randomly allowed even if the
limits specified by the *interval* and *burst* options are exceeded. This is
necessary to prevent an attacker who is sending requests with a spoofed
source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 3 by default, i.e. on average at
least every eighth request has a response. The minimum value is 1 and the
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the
maximum value is 4.
::
+
An example use of the directive is:
+
----
ratelimit interval 4 burst 4
ratelimit interval 1 burst 16
----
+
This would reduce the response rate for IP addresses that send packets on
average more frequently than once per 16 seconds or send packets in bursts
of more than 4 packets.
This would reduce the response rate for IP addresses sending packets on average
more than once per 2 seconds, or sending packets in bursts of more than 16
packets, by up to 75% (with default *leak* of 2).
[[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
The *smoothtime* directive can be used to enable smoothing of the time that
@@ -1391,8 +1400,8 @@ need to be run with the *-p 257* switch to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is
similar to the <<ratelimit,*ratelimit*>> directive, except responses to
localhost are never limited and the default interval is 1 (2 seconds), the default
burst is 16, and the default leak rate is 2.
localhost are never limited and the default interval is -4 (16 packets per
second).
+
An example of the use of the directive is:
+
@@ -1772,17 +1781,18 @@ sendmail binary.
=== Miscellaneous
[[hwtimestamp]]*hwtimestamp* _interface_::
[[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
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.
system clock. In order to get the best results, both sides receiving and
sending NTP packets (i.e. server and client, or two peers) need to use HW
timestamping. If the server or peer supports the interleaved mode, it needs to
be enabled by the *xleave* option in the <<server,*server*>> or the
<<peer,*peer*>> directive.
+
This directive is supported on Linux 3.19 and newer. The NIC must support HW
timestamping, which can be verified with the *ethtool -T* command. The list of
@@ -1799,10 +1809,28 @@ 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:
If the specified interface is _*_, *chronyd* will try to enable HW timestamping
on all available interfaces.
+
The *hwtimestamp* directive has the following options:
+
*txcomp* _compensation_:::
This option specifies the difference in seconds between the actual transmission
time at the physical layer and the reported transmit timestamp. This value will
be added to transmit timestamps obtained from the NIC. The default value is 0.
*rxcomp* _compensation_:::
This option specifies the difference in seconds between the reported receive
timestamp and the actual reception time at the physical layer. This value will
be subtracted from receive timestamps obtained from the NIC. The default value
is 0.
::
+
Examples of the directive are:
+
----
hwtimestamp eth0
hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
----
[[include]]*include* _pattern_::
@@ -2211,24 +2239,34 @@ information to be saved.
*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.
allow client access from all addresses. It is recommended to find at least four
good servers (e.g. from the pool, or on the NTP homepage). If the server has a
hardware reference clock (e.g. a GPS receiver), it can be specified by the
<<refclock,*refclock*>> directive.
The configuration file might be:
The amount of memory used for logging client accesses can be increased in order
to enable clients to use the interleaved mode even when the server has a large
number of clients, and better support rate limiting if it is enabled by the
<<ratelimit,*ratelimit*>> directive. The system timezone database, if it is
kept up to date and includes the *right/UTC* timezone, can be used as a
reliable source to determine when a leap second will be applied to UTC. The
*-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which
*chronyd* will not be able to serve time to its clients when it needs to be
restarted (e.g. after upgrading to a newer version, or a change in the
configuration).
The configuration file could look like:
----
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
pool pool.ntp.org iburst
server qux.example.net iburst
makestep 1.0 3
rtcsync
allow
ratelimit interval 2 burst 10
clientloglimit 100000000
leapsectz right/UTC
driftfile @CHRONYVARDIR@/drift
dumpdir @CHRONYRUNDIR@
dumponexit

View File

@@ -1,6 +1,7 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2016
//
// This program is free software; you can redistribute it and/or modify
@@ -441,10 +442,10 @@ 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_::
[[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.
NTP-specific information about the specified NTP source, or all NTP sources if
no address was specified. An example of the output is shown below.
+
----
Remote address : 203.0.113.15 (CB00710F)
@@ -454,11 +455,11 @@ Leap status : Normal
Version : 4
Mode : Server
Stratum : 1
Poll : 10
Precision : 0.000000060 seconds
Poll interval : 10 (1024 seconds)
Precision : -24 (0.000000060 seconds)
Root delay : 0.000015 seconds
Root dispersion : 0.000015 seconds
Reference ID : 50505331
Reference ID : 47505300 (GPS)
Reference time : Fri Nov 25 15:22:12 2016
Offset : -0.000060878 seconds
Peer delay : 0.000175634 seconds
@@ -489,7 +490,7 @@ reference ID.
*Version*:::
*Mode*:::
*Stratum*:::
*Poll*:::
*Poll interval*:::
*Precision*:::
*Root delay*:::
*Root dispersion*:::

View File

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

View File

@@ -55,6 +55,9 @@ struct HCL_Instance_Record {
/* Number of samples */
int n_samples;
/* Maximum error of the last sample */
double last_err;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
@@ -151,6 +154,7 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
clock->n_samples++;
clock->hw_ref = *hw_ts;
clock->local_ref = *local_ts;
clock->last_err = err;
/* Get new coefficients */
clock->valid_coefs =
@@ -196,9 +200,9 @@ HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
offset = clock->offset + elapsed / clock->frequency;
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
/* Estimation of the error is not implemented yet */
/* Fow now, just return the error of the last sample */
if (err)
*err = 0.0;
*err = clock->last_err;
return 1;
}

View File

@@ -144,6 +144,8 @@ calculate_sys_precision(void)
best *= 2;
}
assert(precision_log >= -30);
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
}

3
main.c
View File

@@ -325,7 +325,8 @@ go_daemon(void)
if (r) {
if (r > 0) {
/* Print the error message from the child */
fprintf(stderr, "%.1024s\n", message);
message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
}
exit(1);
} else

View File

@@ -113,6 +113,9 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
}
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
resolving_threads++;
assert(resolving_threads <= 1);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016
* Copyright (C) Miroslav Lichvar 2009-2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -243,6 +243,11 @@ static ARR_Instance broadcasts;
/* Maximum acceptable delay in transmission for timestamp correction */
#define MAX_TX_DELAY 1.0
/* Maximum allowed values of maxdelay parameters */
#define MAX_MAX_DELAY 1.0e3
#define MAX_MAX_DELAY_RATIO 1.0e6
#define MAX_MAX_DELAY_DEV_RATIO 1.0e6
/* Minimum and maximum allowed poll interval */
#define MIN_POLL 0
#define MAX_POLL 24
@@ -513,9 +518,9 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
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->max_delay = CLAMP(0.0, params->max_delay, MAX_MAX_DELAY);
result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAX_DELAY_RATIO);
result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAX_DELAY_DEV_RATIO);
result->offset_correction = params->offset;
result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target;
@@ -951,57 +956,63 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
frequency all along */
UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz);
/* Prepare random bits which will be added to the transmit timestamp. */
UTI_GetNtp64Fuzz(&ts_fuzz, precision);
do {
/* Prepare random bits which will be added to the transmit timestamp */
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, &local_transmit_err);
/* 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, &local_transmit_err);
if (smooth_time)
UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
if (smooth_time)
UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
length = NTP_NORMAL_PACKET_LENGTH;
length = NTP_NORMAL_PACKET_LENGTH;
/* Authenticate the packet if needed */
/* Authenticate the packet */
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_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_mode == AUTH_MSSNTP) {
/* Pre-compensate the transmit time by approximately how long it will
take to generate the authentication data */
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;
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);
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);
}
message.auth_keyid = htonl(key_id);
mac_len = sizeof (message.auth_keyid) + auth_len;
/* Truncate MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */
if (version == 4 && mac_len > NTP_MAX_V4_MAC_LENGTH)
mac_len = NTP_MAX_V4_MAC_LENGTH;
length += mac_len;
} 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 {
UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
&message.transmit_ts, &ts_fuzz);
}
} else {
UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
&message.transmit_ts, &ts_fuzz);
}
/* Avoid sending messages with non-zero transmit timestamp equal to the
receive timestamp to allow reliable detection of the interleaved mode */
} while (!UTI_CompareNtp64(&message.transmit_ts, &message.receive_ts) &&
!UTI_IsZeroNtp64(&message.transmit_ts));
ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
@@ -1412,12 +1423,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
sample_rx_tss = rx_ts->source;
}
/* 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);
/* Calculate delay */
delay = fabs(local_interval - remote_interval);
if (delay < precision)
delay = precision;
@@ -1439,7 +1446,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
skew = (source_freq_hi - source_freq_lo) / 2.0;
/* and then calculate peer dispersion */
dispersion = precision + inst->local_tx.err + rx_ts_err + skew * fabs(local_interval);
dispersion = MAX(precision, MAX(inst->local_tx.err, rx_ts_err)) +
skew * fabs(local_interval);
/* Additional tests required to pass before accumulating the sample */
@@ -1453,11 +1461,12 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
!(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 */
/* Test B requires in the basic client mode that the ratio of the round
trip delay to the minimum one currently in the stats data register is
less than an administrator-defined value */
testB = inst->max_delay_ratio <= 1.0 ||
(delay - dispersion) / SST_MinRoundTripDelay(stats) <= inst->max_delay_ratio;
!(inst->mode == MODE_CLIENT && !interleaved_packet &&
(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
@@ -1608,13 +1617,14 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts));
if (kod_rate) {
LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s",
UTI_IPToString(&inst->remote_addr.ip_addr));
/* Back off for a while and stop ongoing burst */
delay_time += 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));
}
}
@@ -2107,9 +2117,9 @@ NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
void
NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
{
inst->max_delay = new_max_delay;
inst->max_delay = CLAMP(0.0, new_max_delay, MAX_MAX_DELAY);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay);
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay);
}
/* ================================================== */
@@ -2117,9 +2127,9 @@ NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
void
NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
{
inst->max_delay_ratio = new_max_delay_ratio;
inst->max_delay_ratio = CLAMP(0.0, new_max_delay_ratio, MAX_MAX_DELAY_RATIO);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay ratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay_ratio);
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_ratio);
}
/* ================================================== */
@@ -2127,9 +2137,9 @@ NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
void
NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio)
{
inst->max_delay_dev_ratio = new_max_delay_dev_ratio;
inst->max_delay_dev_ratio = CLAMP(0.0, new_max_delay_dev_ratio, MAX_MAX_DELAY_DEV_RATIO);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay dev ratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay_dev_ratio);
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_dev_ratio);
}
/* ================================================== */

View File

@@ -364,8 +364,12 @@ NIO_Initialise(int family)
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise();
#else
if (ARR_GetSize(CNF_GetHwTsInterfaces()))
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
if (1) {
double tx_comp, rx_comp;
char *name;
if (CNF_GetHwTsInterface(0, &name, &tx_comp, &rx_comp))
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
}
#endif
recv_messages = ARR_CreateInstance(sizeof (struct Message));

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
* Copyright (C) Miroslav Lichvar 2016-2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -28,6 +28,7 @@
#include "sysincl.h"
#include <ifaddrs.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
@@ -65,12 +66,18 @@ struct Interface {
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
#define MAX_TS_DELAY 1.0
/* Array of Interfaces */
static ARR_Instance interfaces;
@@ -84,15 +91,22 @@ static int permanent_ts_options;
/* ================================================== */
static int
add_interface(const char *name)
add_interface(const char *name, double tx_comp, double rx_comp)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_index, phc_fd;
unsigned int i;
struct Interface *iface;
char phc_path[64];
/* Check if the interface was not already added */
for (i = 0; i < ARR_GetSize(interfaces); i++) {
if (!strcmp(name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
return 1;
}
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
@@ -158,13 +172,42 @@ add_interface(const char *name)
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->tx_comp = tx_comp;
iface->rx_comp = rx_comp;
iface->clock = HCL_CreateInstance();
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", name);
return 1;
}
/* ================================================== */
static int
add_all_interfaces(double tx_comp, double rx_comp)
{
struct ifaddrs *ifaddr, *ifa;
int r;
if (getifaddrs(&ifaddr)) {
DEBUG_LOG(LOGF_NtpIOLinux, "getifaddrs() failed : %s", strerror(errno));
return 0;
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (add_interface(ifa->ifa_name, tx_comp, rx_comp))
r = 1;
}
freeifaddrs(ifaddr);
/* Return success if at least one interface was added */
return r;
}
/* ================================================== */
static void
update_interface_speed(struct Interface *iface)
{
@@ -199,22 +242,33 @@ update_interface_speed(struct Interface *iface)
void
NIO_Linux_Initialise(void)
{
ARR_Instance config_hwts_ifaces;
char *if_name;
double tx_comp, rx_comp;
char *name;
unsigned int i;
int hwts;
interfaces = ARR_CreateInstance(sizeof (struct Interface));
config_hwts_ifaces = CNF_GetHwTsInterfaces();
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
/* 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);
}
for (i = hwts = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (!strcmp("*", name))
continue;
if (!add_interface(name, tx_comp, rx_comp))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", name);
hwts = 1;
}
for (i = 0; CNF_GetHwTsInterface(i, &name, &tx_comp, &rx_comp); i++) {
if (strcmp("*", name))
continue;
if (add_all_interfaces(tx_comp, rx_comp))
hwts = 1;
break;
}
if (hwts) {
ts_flags = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_HARDWARE;
} else {
@@ -368,8 +422,8 @@ 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;
struct timespec sample_phc_ts, sample_local_ts, ts;
double sample_delay, rx_correction, ts_delay, err;
int l2_length;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
@@ -395,9 +449,23 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
}
if (!HCL_CookTime(iface->clock, hw_ts, &local_ts->ts, &local_ts->err))
if (!rx_ntp_length && iface->tx_comp)
UTI_AddDoubleToTimespec(hw_ts, iface->tx_comp, hw_ts);
else if (rx_ntp_length && iface->rx_comp)
UTI_AddDoubleToTimespec(hw_ts, -iface->rx_comp, hw_ts);
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &err))
return;
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG(LOGF_NtpIOLinux, "Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = err;
local_ts->source = NTP_TS_HARDWARE;
}
@@ -487,7 +555,7 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
if (!UTI_IsZeroTimespec(&ts3.ts[0])) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
} else {
} else if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(if_index);
if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,

View File

@@ -187,6 +187,11 @@ accumulate_sample(time_t rtc, struct timespec *sys)
discard_samples(NEW_FIRST_WHEN_FULL);
}
/* Discard all samples if the RTC was stepped back (not our trim) */
if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) {
DEBUG_LOG(LOGF_RtcLinux, "RTC samples discarded");
n_samples = 0;
}
/* Always use most recent sample as reference */
/* use sample only if n_sample is not negative*/

View File

@@ -425,10 +425,11 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
}
/* Try to replace NTP sources that are unreachable, falsetickers, or
have root distance larger than the allowed maximum */
have root distance or jitter larger than the allowed maximums */
if (inst->type == SRC_NTP &&
((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) ||
inst->status == SRC_FALSETICKER || inst->status == SRC_BAD_DISTANCE)) {
inst->status == SRC_BAD_DISTANCE || inst->status == SRC_JITTERY ||
inst->status == SRC_FALSETICKER)) {
NSR_HandleBadSource(inst->ip_addr);
}
}

View File

@@ -47,8 +47,12 @@
2000ppm, which would be pretty bad */
#define WORST_CASE_FREQ_BOUND (2000.0/1.0e6)
/* The minimum allowed skew */
/* The minimum and maximum assumed skew */
#define MIN_SKEW 1.0e-12
#define MAX_SKEW 1.0e+02
/* The minimum assumed std dev for weighting */
#define MIN_WEIGHT_SD 1.0e-9
/* The asymmetry of network jitter when all jitter is in one direction */
#define MAX_ASYMMETRY 0.5
@@ -507,8 +511,7 @@ SST_DoNewRegression(SST_Stats inst)
/* And now, work out the weight vector */
sd = mean_distance - min_distance;
if (sd > min_distance || sd <= 0.0)
sd = min_distance;
sd = CLAMP(MIN_WEIGHT_SD, sd, min_distance);
for (i=0; i<inst->n_samples; i++) {
sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / sd;
@@ -539,9 +542,7 @@ SST_DoNewRegression(SST_Stats inst)
inst->std_dev = sqrt(est_var);
inst->nruns = nruns;
if (inst->skew < MIN_SKEW)
inst->skew = MIN_SKEW;
inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
DEBUG_LOG(LOGF_SourceStats, "off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
@@ -844,6 +845,8 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
inst->strata[j]);
}
fprintf(out, "%d\n", inst->asymmetry_run);
}
/* ================================================== */
@@ -898,6 +901,10 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
UTI_NormaliseTimespec(&inst->sample_times[i]);
}
}
/* This field was not saved in older versions */
if (!fgets(line, sizeof(line), in) || sscanf(line, "%d\n", &inst->asymmetry_run) != 1)
inst->asymmetry_run = 0;
} else {
inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0;
@@ -921,7 +928,7 @@ void
SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now)
{
int i, j;
struct timespec ago;
struct timespec last_sample_time;
if (inst->n_samples > 0) {
i = get_runsbuf_index(inst, inst->n_samples - 1);
@@ -931,8 +938,10 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j];
UTI_DiffTimespecs(&ago, now, &inst->sample_times[i]);
report->latest_meas_ago = ago.tv_sec;
/* Align the sample time to reduce the leak of the receive timestamp */
last_sample_time = inst->sample_times[i];
last_sample_time.tv_nsec = 0;
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else {
report->latest_meas_ago = (uint32_t)-1;
report->orig_latest_meas = 0;

20
stubs.c
View File

@@ -42,6 +42,7 @@
#include "privops.h"
#include "refclock.h"
#include "sched.h"
#include "util.h"
#ifndef FEAT_ASYNCDNS
@@ -51,10 +52,11 @@ struct DNS_Async_Instance {
const char *name;
DNS_NameResolveHandler handler;
void *arg;
int pipe[2];
};
static void
resolve_name(void *anything)
resolve_name(int fd, int event, void *anything)
{
struct DNS_Async_Instance *inst;
IPAddr addrs[DNS_MAX_ADDRESSES];
@@ -62,6 +64,11 @@ resolve_name(void *anything)
int i;
inst = (struct DNS_Async_Instance *)anything;
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES);
for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES &&
@@ -83,7 +90,16 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->handler = handler;
inst->arg = anything;
SCH_AddTimeoutByDelay(0.0, resolve_name, inst);
if (pipe(inst->pipe))
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, resolve_name, inst);
if (write(inst->pipe[1], "", 1) < 0)
;
}
#endif /* !FEAT_ASYNCDNS */

View File

@@ -158,7 +158,8 @@ set_sync_status(int synchronised, double est_error, double max_error)
txc.esterror = est_error * 1.0e6;
txc.maxerror = max_error * 1.0e6;
SYS_Timex_Adjust(&txc, 1);
if (SYS_Timex_Adjust(&txc, 1) < 0)
;
}
/* ================================================== */

View File

@@ -25,7 +25,7 @@ time_offset=-10
client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100"
min_sync_time=8000
max_sync_time=8500
max_sync_time=9000
run_test || test_fail
check_chronyd_exit || test_fail

296
test/unit/ntp_core.c Normal file
View File

@@ -0,0 +1,296 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
*/
#include <config.h>
#include <sysincl.h>
#include <cmdparse.h>
#include <conf.h>
#include <keys.h>
#include <ntp_io.h>
#include <sched.h>
#include <local.h>
#include "test.h"
static struct timespec current_time;
static NTP_Receive_Buffer req_buffer, res_buffer;
static int req_length, res_length;
#define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0)
#define NIO_CloseServerSocket(fd) assert(fd == 100)
#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
#define NIO_CloseClientSocket(fd) assert(fd == 101)
#define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1)
#define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1))
#define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \
add_timeout_in_class(delay, separation, randomness, class, handler, arg)
#define SCH_RemoveTimeout(id) assert(!id || id == 102)
#define LCL_ReadRawTime(ts) (*ts = current_time)
#define LCL_ReadCookedTime(ts, err) do {double *p = err; *ts = current_time; if (p) *p = 0.0;} while (0)
#define SRC_UpdateReachability(inst, reach)
#define SRC_ResetReachability(inst)
static SCH_TimeoutID
add_timeout_in_class(double min_delay, double separation, double randomness,
SCH_TimeoutClass class, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{
return 102;
}
#include <ntp_core.c>
static NCR_Instance inst;
static void
advance_time(double x)
{
UTI_AddDoubleToTimespec(&current_time, x, &current_time);
}
static void
send_request(void)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
uint32_t prev_tx_count;
prev_tx_count = inst->report.total_tx_count;
transmit_timeout(inst);
TEST_CHECK(!inst->valid_rx);
TEST_CHECK(!inst->updated_timestamps);
TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count);
advance_time(1e-4);
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_DAEMON;
NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length);
}
static void
send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth)
{
NTP_Packet *req, *res;
req = &req_buffer.ntp_pkt;
res = &res_buffer.ntp_pkt;
TEST_CHECK(req_length >= NTP_NORMAL_PACKET_LENGTH);
res->lvm = NTP_LVM(LEAP_Normal, NTP_LVM_TO_VERSION(req->lvm),
NTP_LVM_TO_MODE(req->lvm) == MODE_CLIENT ? MODE_SERVER : MODE_ACTIVE);
res->stratum = 1;
res->poll = req->poll;
res->precision = -20;
res->root_delay = UTI_DoubleToNtp32(0.1);
res->root_dispersion = UTI_DoubleToNtp32(0.1);
res->reference_id = 0;
UTI_ZeroNtp64(&res->reference_ts);
res->originate_ts = interleaved ? req->receive_ts : req->transmit_ts;
advance_time(TST_GetRandomDouble(1e-4, 1e-2));
UTI_TimespecToNtp64(&current_time, &res->receive_ts, NULL);
advance_time(TST_GetRandomDouble(-1e-4, 1e-3));
UTI_TimespecToNtp64(&current_time, &res->transmit_ts, NULL);
advance_time(TST_GetRandomDouble(1e-4, 1e-2));
if (!valid_ts) {
switch (random() % (allow_update ? 4 : 5)) {
case 0:
res->originate_ts.hi = random();
break;
case 1:
res->originate_ts.lo = random();
break;
case 2:
UTI_ZeroNtp64(&res->originate_ts);
break;
case 3:
UTI_ZeroNtp64(&res->receive_ts);
break;
case 4:
UTI_ZeroNtp64(&res->transmit_ts);
break;
default:
assert(0);
}
}
if (authenticated) {
res->auth_keyid = req->auth_keyid;
KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, NTP_NORMAL_PACKET_LENGTH,
res->auth_data, 16);
res_length = NTP_NORMAL_PACKET_LENGTH + 4 + 16;
} else {
res_length = NTP_NORMAL_PACKET_LENGTH;
}
if (!valid_auth) {
switch (random() % 3) {
case 0:
res->auth_keyid++;
break;
case 1:
res->auth_data[random() % 16]++;
break;
case 2:
res_length = NTP_NORMAL_PACKET_LENGTH;
break;
default:
assert(0);
}
}
}
static void
process_response(int valid, int updated)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
NTP_Packet *res;
uint32_t prev_rx_count, prev_valid_count;
struct timespec prev_rx_ts;
int prev_open_socket;
res = &res_buffer.ntp_pkt;
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) == MODE_ACTIVE ? 100 : 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_DAEMON;
prev_rx_count = inst->report.total_rx_count;
prev_valid_count = inst->report.total_valid_count;
prev_rx_ts = inst->local_rx.ts;
prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD;
NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
if (prev_open_socket)
TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count);
else
TEST_CHECK(prev_rx_count == inst->report.total_rx_count);
if (valid)
TEST_CHECK(prev_valid_count + 1 == inst->report.total_valid_count);
else
TEST_CHECK(prev_valid_count == inst->report.total_valid_count);
if (updated)
TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
else
TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
}
void
test_unit(void)
{
char source_line[] = "127.0.0.1";
char conf[][100] = {
"port 0",
"keyfile ntp_core.keys"
};
int i, j, interleaved, authenticated, valid, updated, has_updated;
CPS_NTP_Source source;
NTP_Remote_Address remote_addr;
CNF_Initialise(0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
LCL_Initialise();
TST_RegisterDummyDrivers();
SCH_Initialise();
SRC_Initialise();
NIO_Initialise(IPADDR_UNSPEC);
NCR_Initialise();
REF_Initialise();
KEY_Initialise();
for (i = 0; i < 1000; i++) {
CPS_ParseNTPSourceAdd(source_line, &source);
if (random() % 2)
source.params.interleaved = 1;
if (random() % 2)
source.params.authkey = 1;
UTI_ZeroTimespec(&current_time);
advance_time(TST_GetRandomDouble(1.0, 1e9));
TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
remote_addr.port = 123;
inst = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
NCR_StartInstance(inst);
has_updated = 0;
for (j = 0; j < 50; j++) {
DEBUG_LOG(0, "iteration %d, %d", i, j);
interleaved = random() % 2;
authenticated = random() % 2;
valid = (!interleaved || (source.params.interleaved && has_updated)) &&
(!source.params.authkey || authenticated);
updated = (valid || inst->mode == MODE_ACTIVE) &&
(!source.params.authkey || authenticated);
has_updated = has_updated || updated;
send_request();
send_response(interleaved, authenticated, 1, 0, 1);
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0);
process_response(0, 0);
}
send_response(interleaved, authenticated, 1, 1, 1);
process_response(valid, updated);
process_response(0, 0);
advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1);
process_response(0, 0);
advance_time(1.0);
send_response(interleaved, authenticated, 1, 1, 1);
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
}
NCR_DestroyInstance(inst);
}
KEY_Finalise();
REF_Finalise();
NCR_Finalise();
NIO_Finalise();
SRC_Finalise();
SCH_Finalise();
LCL_Finalise();
CNF_Finalise();
}

2
test/unit/ntp_core.keys Normal file
View File

@@ -0,0 +1,2 @@
1 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
2 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579

View File

@@ -74,6 +74,14 @@ void test_unit(void) {
TEST_CHECK(!UTI_IsZeroTimespec(&ts));
TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
ntp_ts.hi = 0;
ntp_ts.lo = 0;
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
TEST_CHECK(UTI_IsZeroTimespec(&ts));
UTI_TimespecToNtp64(&ts, &ntp_ts, NULL);
TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
ntp_fuzz.hi = htonl(1);
ntp_fuzz.lo = htonl(3);
ntp_ts.hi = htonl(1);

7
util.c
View File

@@ -754,8 +754,11 @@ UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
{
uint32_t ntp_sec, ntp_frac;
/* As yet, there is no need to check for zero - all processing that
has to detect that case is in the NTP layer */
/* Zero is a special value */
if (UTI_IsZeroNtp64(src)) {
UTI_ZeroTimespec(dest);
return;
}
ntp_sec = ntohl(src->hi);
ntp_frac = ntohl(src->lo);