Compare commits

..

12 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
18 changed files with 410 additions and 70 deletions

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 Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of 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. sub-microsecond accuracy may be possible.
Two programs are included in chrony, chronyd is a daemon that can be Two programs are included in chrony, chronyd is a daemon that can be
@@ -203,6 +203,9 @@ Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de> Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values Handling arbitrary HZ values
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr> Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc Patch to add refresh command to chronyc

View File

@@ -3,6 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016
* Copyright (C) Miroslav Lichvar 2009-2016 * Copyright (C) Miroslav Lichvar 2009-2016
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -2723,7 +2724,8 @@ process_cmd_waitsync(char *line)
max_skew_ppm = 0.0; max_skew_ppm = 0.0;
interval = 10.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 */ /* Don't allow shorter interval than 0.1 seconds */
if (interval < 0.1) if (interval < 0.1)
@@ -2831,7 +2833,8 @@ process_cmd_keygen(char *line)
snprintf(hash_name, sizeof (hash_name), "MD5"); snprintf(hash_name, sizeof (hash_name), "MD5");
#endif #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)); length = CLAMP(10, (bits + 7) / 8, sizeof (key));
if (HSH_GetHashId(hash_name) < 0) { if (HSH_GetHashId(hash_name) < 0) {
@@ -3094,7 +3097,7 @@ static void
display_gpl(void) display_gpl(void)
{ {
printf("chrony version %s\n" 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" "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n" "you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n", "GNU General Public License version 2 for details.\n\n",

2
conf.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as

3
configure vendored
View File

@@ -4,7 +4,8 @@
# chronyd/chronyc - Programs for keeping computer clocks accurate. # chronyd/chronyc - Programs for keeping computer clocks accurate.
# #
# Copyright (C) Richard P. Curnow 1997-2003 # 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
# #
# ======================================================================= # =======================================================================

View File

@@ -1,7 +1,8 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // 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 // 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 // it under the terms of version 2 of the GNU General Public License as
@@ -105,7 +106,7 @@ 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* measurement to be ignored, this level can be defined with the *maxdelay*
option. For example, *maxdelay 0.3* would indicate that measurements with a 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 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_::: *maxdelayratio* _ratio_:::
This option is similar to the maxdelay option above. *chronyd* keeps a record This option is similar to the maxdelay option above. *chronyd* keeps a record
of the minimum round-trip delay amongst the previous measurements that it has of the minimum round-trip delay amongst the previous measurements that it has

View File

@@ -1,6 +1,7 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2016 // Copyright (C) Miroslav Lichvar 2009-2016
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify

View File

@@ -144,6 +144,8 @@ calculate_sys_precision(void)
best *= 2; best *= 2;
} }
assert(precision_log >= -30);
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log); 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) {
if (r > 0) { if (r > 0) {
/* Print the error message from the child */ /* Print the error message from the child */
fprintf(stderr, "%.1024s\n", message); message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
} }
exit(1); exit(1);
} else } else

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * 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 * 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 */ /* Maximum acceptable delay in transmission for timestamp correction */
#define MAX_TX_DELAY 1.0 #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 */ /* Minimum and maximum allowed poll interval */
#define MIN_POLL 0 #define MIN_POLL 0
#define MAX_POLL 24 #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) if (result->presend_minpoll <= MAX_POLL && result->mode != MODE_CLIENT)
result->presend_minpoll = MAX_POLL + 1; result->presend_minpoll = MAX_POLL + 1;
result->max_delay = params->max_delay; result->max_delay = CLAMP(0.0, params->max_delay, MAX_MAX_DELAY);
result->max_delay_ratio = params->max_delay_ratio; result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAX_DELAY_RATIO);
result->max_delay_dev_ratio = params->max_delay_dev_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->offset_correction = params->offset;
result->auto_offline = params->auto_offline; result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target; 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 */ frequency all along */
UTI_TimespecToNtp64(&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. */ do {
UTI_GetNtp64Fuzz(&ts_fuzz, precision); /* 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 /* 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 store this for our own use later, next time we receive a message
from the source we're sending to now. */ from the source we're sending to now. */
LCL_ReadCookedTime(&local_transmit, &local_transmit_err); LCL_ReadCookedTime(&local_transmit, &local_transmit_err);
if (smooth_time) if (smooth_time)
UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit); 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) { if (auth_mode == AUTH_SYMMETRIC || auth_mode == AUTH_MSSNTP) {
/* Pre-compensate the transmit time by approx. how long it will /* Pre-compensate the transmit time by approximately how long it will
take to generate the authentication data. */ take to generate the authentication data */
local_transmit.tv_nsec += auth_mode == AUTH_SYMMETRIC ? local_transmit.tv_nsec += auth_mode == AUTH_SYMMETRIC ?
KEY_GetAuthDelay(key_id) : NSD_GetAuthDelay(key_id); KEY_GetAuthDelay(key_id) : NSD_GetAuthDelay(key_id);
UTI_NormaliseTimespec(&local_transmit); UTI_NormaliseTimespec(&local_transmit);
UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit, UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
&message.transmit_ts, &ts_fuzz); &message.transmit_ts, &ts_fuzz);
if (auth_mode == AUTH_SYMMETRIC) { if (auth_mode == AUTH_SYMMETRIC) {
auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message, auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
offsetof(NTP_Packet, auth_keyid), offsetof(NTP_Packet, auth_keyid),
(unsigned char *)&message.auth_data, (unsigned char *)&message.auth_data,
sizeof (message.auth_data)); sizeof (message.auth_data));
if (!auth_len) { if (!auth_len) {
DEBUG_LOG(LOGF_NtpCore, "Could not generate auth data with key %"PRIu32, key_id); DEBUG_LOG(LOGF_NtpCore, "Could not generate auth data with key %"PRIu32, key_id);
return 0; 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);
} }
} else {
message.auth_keyid = htonl(key_id); UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
mac_len = sizeof (message.auth_keyid) + auth_len; &message.transmit_ts, &ts_fuzz);
/* 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, /* Avoid sending messages with non-zero transmit timestamp equal to the
&message.transmit_ts, &ts_fuzz); 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); ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
@@ -2106,9 +2117,9 @@ NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
void void
NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay) 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", 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);
} }
/* ================================================== */ /* ================================================== */
@@ -2116,9 +2127,9 @@ NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
void void
NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio) 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", 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);
} }
/* ================================================== */ /* ================================================== */
@@ -2126,9 +2137,9 @@ NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
void void
NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio) 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", 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

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. 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 * 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 * it under the terms of version 2 of the GNU General Public License as

View File

@@ -187,6 +187,11 @@ accumulate_sample(time_t rtc, struct timespec *sys)
discard_samples(NEW_FIRST_WHEN_FULL); 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 */ /* Always use most recent sample as reference */
/* use sample only if n_sample is not negative*/ /* use sample only if n_sample is not negative*/

View File

@@ -928,7 +928,7 @@ void
SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now) SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now)
{ {
int i, j; int i, j;
struct timespec ago; struct timespec last_sample_time;
if (inst->n_samples > 0) { if (inst->n_samples > 0) {
i = get_runsbuf_index(inst, inst->n_samples - 1); i = get_runsbuf_index(inst, inst->n_samples - 1);
@@ -938,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->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j]; report->stratum = inst->strata[j];
UTI_DiffTimespecs(&ago, now, &inst->sample_times[i]); /* Align the sample time to reduce the leak of the receive timestamp */
report->latest_meas_ago = ago.tv_sec; last_sample_time = inst->sample_times[i];
last_sample_time.tv_nsec = 0;
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else { } else {
report->latest_meas_ago = (uint32_t)-1; report->latest_meas_ago = (uint32_t)-1;
report->orig_latest_meas = 0; report->orig_latest_meas = 0;

View File

@@ -158,7 +158,8 @@ set_sync_status(int synchronised, double est_error, double max_error)
txc.esterror = est_error * 1.0e6; txc.esterror = est_error * 1.0e6;
txc.maxerror = max_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_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100" client_conf="corrtimeratio 100"
min_sync_time=8000 min_sync_time=8000
max_sync_time=8500 max_sync_time=9000
run_test || test_fail run_test || test_fail
check_chronyd_exit || 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_IsZeroTimespec(&ts));
TEST_CHECK(!UTI_IsZeroNtp64(&ntp_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.hi = htonl(1);
ntp_fuzz.lo = htonl(3); ntp_fuzz.lo = htonl(3);
ntp_ts.hi = htonl(1); 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; uint32_t ntp_sec, ntp_frac;
/* As yet, there is no need to check for zero - all processing that /* Zero is a special value */
has to detect that case is in the NTP layer */ if (UTI_IsZeroNtp64(src)) {
UTI_ZeroTimespec(dest);
return;
}
ntp_sec = ntohl(src->hi); ntp_sec = ntohl(src->hi);
ntp_frac = ntohl(src->lo); ntp_frac = ntohl(src->lo);