clientlog: separate NTP timestamps from IP addresses
Instead of keeping one pair of RX and TX timestamp for each address, add a separate RX->TX map using an ordered circular buffer. Save the RX timestamps as 64-bit integers and search them with a combined linear interpolation and binary algorithm. This enables the server to support multiple interleaved clients sharing the same IP address (e.g. NAT) and it will allow other improvements to be implemented later. A drawback is that a single broken client sending interleaved requests at a high rate (without spoofing the source address) can now prevent clients on other addresses from getting interleaved responses. The total number of saved timestamps does not change. It's still determined by the clientloglimit directive. A new option may be added later if needed. The whole buffer is allocated at once, but only on first use to not waste memory on client-only configurations.
This commit is contained in:
parent
5cb469b204
commit
14b8df3702
5 changed files with 519 additions and 40 deletions
331
clientlog.c
331
clientlog.c
|
@ -55,8 +55,6 @@ typedef struct {
|
|||
int8_t rate[MAX_SERVICES];
|
||||
int8_t ntp_timeout_rate;
|
||||
uint8_t drop_flags;
|
||||
NTP_int64 ntp_rx_ts;
|
||||
NTP_int64 ntp_tx_ts;
|
||||
} Record;
|
||||
|
||||
/* Hash table of records, there is a fixed number of records per slot */
|
||||
|
@ -124,6 +122,35 @@ static int limit_interval[MAX_SERVICES];
|
|||
/* Flag indicating whether facility is turned on or not */
|
||||
static int active;
|
||||
|
||||
/* RX and TX timestamp saved for clients using interleaved mode */
|
||||
typedef struct {
|
||||
uint64_t rx_ts;
|
||||
uint32_t flags;
|
||||
int32_t tx_ts_offset;
|
||||
} NtpTimestamps;
|
||||
|
||||
/* Flags for NTP timestamps */
|
||||
#define NTPTS_DISABLED 1
|
||||
#define NTPTS_VALID_TX 2
|
||||
|
||||
/* RX->TX map using a circular buffer with ordered timestamps */
|
||||
typedef struct {
|
||||
ARR_Instance timestamps;
|
||||
uint32_t first;
|
||||
uint32_t size;
|
||||
uint32_t max_size;
|
||||
uint32_t cached_index;
|
||||
uint64_t cached_rx_ts;
|
||||
} NtpTimestampMap;
|
||||
|
||||
static NtpTimestampMap ntp_ts_map;
|
||||
|
||||
/* Maximum interval of NTP timestamps in future after a backward step */
|
||||
#define NTPTS_FUTURE_LIMIT (1LL << 32) /* 1 second */
|
||||
|
||||
/* Maximum number of timestamps moved in the array to insert a new timestamp */
|
||||
#define NTPTS_INSERT_LIMIT 64
|
||||
|
||||
/* Global statistics */
|
||||
static uint32_t total_hits[MAX_SERVICES];
|
||||
static uint32_t total_drops[MAX_SERVICES];
|
||||
|
@ -229,8 +256,6 @@ get_record(IPAddr *ip)
|
|||
record->rate[i] = INVALID_RATE;
|
||||
record->ntp_timeout_rate = INVALID_RATE;
|
||||
record->drop_flags = 0;
|
||||
UTI_ZeroNtp64(&record->ntp_rx_ts);
|
||||
UTI_ZeroNtp64(&record->ntp_tx_ts);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
@ -359,7 +384,8 @@ CLG_Initialise(void)
|
|||
/* Calculate the maximum number of slots that can be allocated in the
|
||||
configured memory limit. Take into account expanding of the hash
|
||||
table where two copies exist at the same time. */
|
||||
max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2);
|
||||
max_slots = CNF_GetClientLogLimit() /
|
||||
((sizeof (Record) + sizeof (NtpTimestamps)) * SLOT_SIZE * 3 / 2);
|
||||
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
|
||||
for (slots2 = 0; 1U << (slots2 + 1) <= max_slots; slots2++)
|
||||
;
|
||||
|
@ -373,6 +399,13 @@ CLG_Initialise(void)
|
|||
|
||||
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
|
||||
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
|
||||
|
||||
ntp_ts_map.timestamps = NULL;
|
||||
ntp_ts_map.first = 0;
|
||||
ntp_ts_map.size = 0;
|
||||
ntp_ts_map.max_size = 1U << (slots2 + SLOT_BITS);
|
||||
ntp_ts_map.cached_index = 0;
|
||||
ntp_ts_map.cached_rx_ts = 0ULL;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -384,6 +417,8 @@ CLG_Finalise(void)
|
|||
return;
|
||||
|
||||
ARR_DestroyInstance(records);
|
||||
if (ntp_ts_map.timestamps)
|
||||
ARR_DestroyInstance(ntp_ts_map.timestamps);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -598,22 +633,294 @@ CLG_LogAuthNtpRequest(void)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
|
||||
int
|
||||
CLG_GetNtpMinPoll(void)
|
||||
{
|
||||
Record *record;
|
||||
return limit_interval[CLG_NTP];
|
||||
}
|
||||
|
||||
record = ARR_GetElement(records, index);
|
||||
/* ================================================== */
|
||||
|
||||
*rx_ts = &record->ntp_rx_ts;
|
||||
*tx_ts = &record->ntp_tx_ts;
|
||||
static NtpTimestamps *
|
||||
get_ntp_tss(uint32_t index)
|
||||
{
|
||||
return ARR_GetElement(ntp_ts_map.timestamps,
|
||||
(ntp_ts_map.first + index) & (ntp_ts_map.max_size - 1));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
find_ntp_rx_ts(uint64_t rx_ts, uint32_t *index)
|
||||
{
|
||||
uint64_t rx_x, rx_lo, rx_hi, step;
|
||||
uint32_t i, x, lo, hi;
|
||||
|
||||
if (ntp_ts_map.cached_rx_ts == rx_ts && rx_ts != 0ULL) {
|
||||
*index = ntp_ts_map.cached_index;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ntp_ts_map.size == 0) {
|
||||
*index = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lo = 0;
|
||||
hi = ntp_ts_map.size - 1;
|
||||
rx_lo = get_ntp_tss(lo)->rx_ts;
|
||||
rx_hi = get_ntp_tss(hi)->rx_ts;
|
||||
|
||||
/* Check for ts < lo before ts > hi to trim timestamps from "future" later
|
||||
if both conditions are true to not break the order of the endpoints.
|
||||
Compare timestamps by their difference to allow adjacent NTP eras. */
|
||||
if ((int64_t)(rx_ts - rx_lo) < 0) {
|
||||
*index = 0;
|
||||
return 0;
|
||||
} else if ((int64_t)(rx_ts - rx_hi) > 0) {
|
||||
*index = ntp_ts_map.size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform a combined linear interpolation and binary search */
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
if (rx_ts == rx_hi) {
|
||||
*index = ntp_ts_map.cached_index = hi;
|
||||
ntp_ts_map.cached_rx_ts = rx_ts;
|
||||
return 1;
|
||||
} else if (rx_ts == rx_lo) {
|
||||
*index = ntp_ts_map.cached_index = lo;
|
||||
ntp_ts_map.cached_rx_ts = rx_ts;
|
||||
return 1;
|
||||
} else if (lo + 1 == hi) {
|
||||
*index = hi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hi - lo > 3 && i % 2 == 0) {
|
||||
step = (rx_hi - rx_lo) / (hi - lo);
|
||||
if (step == 0)
|
||||
step = 1;
|
||||
x = lo + (rx_ts - rx_lo) / step;
|
||||
} else {
|
||||
x = lo + (hi - lo) / 2;
|
||||
}
|
||||
|
||||
if (x <= lo)
|
||||
x = lo + 1;
|
||||
else if (x >= hi)
|
||||
x = hi - 1;
|
||||
|
||||
rx_x = get_ntp_tss(x)->rx_ts;
|
||||
|
||||
if ((int64_t)(rx_x - rx_ts) <= 0) {
|
||||
lo = x;
|
||||
rx_lo = rx_x;
|
||||
} else {
|
||||
hi = x;
|
||||
rx_hi = rx_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static uint64_t
|
||||
ntp64_to_int64(NTP_int64 *ts)
|
||||
{
|
||||
return (uint64_t)ntohl(ts->hi) << 32 | ntohl(ts->lo);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
|
||||
{
|
||||
ntp_ts->hi = htonl(ts >> 32);
|
||||
ntp_ts->lo = htonl(ts & ((1ULL << 32) - 1));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static uint32_t
|
||||
push_ntp_tss(uint32_t index)
|
||||
{
|
||||
if (ntp_ts_map.size < ntp_ts_map.max_size) {
|
||||
ntp_ts_map.size++;
|
||||
} else {
|
||||
ntp_ts_map.first = (ntp_ts_map.first + 1) % (ntp_ts_map.max_size);
|
||||
if (index > 0)
|
||||
index--;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
if (!tx_ts) {
|
||||
tss->flags &= ~NTPTS_VALID_TX;
|
||||
return;
|
||||
}
|
||||
|
||||
UTI_Ntp64ToTimespec(rx_ts, &ts);
|
||||
UTI_DiffTimespecs(&ts, tx_ts, &ts);
|
||||
|
||||
if (ts.tv_sec < -2 || ts.tv_sec > 1) {
|
||||
tss->flags &= ~NTPTS_VALID_TX;
|
||||
return;
|
||||
}
|
||||
|
||||
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
|
||||
tss->flags |= NTPTS_VALID_TX;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
|
||||
{
|
||||
int32_t offset = tss->tx_ts_offset;
|
||||
NTP_int64 ntp_ts;
|
||||
|
||||
if (tss->flags & NTPTS_VALID_TX) {
|
||||
int64_to_ntp64(tss->rx_ts, &ntp_ts);
|
||||
UTI_Ntp64ToTimespec(&ntp_ts, tx_ts);
|
||||
if (offset >= (int32_t)NSEC_PER_SEC) {
|
||||
offset -= NSEC_PER_SEC;
|
||||
tx_ts->tv_sec++;
|
||||
}
|
||||
tx_ts->tv_nsec += offset;
|
||||
UTI_NormaliseTimespec(tx_ts);
|
||||
} else {
|
||||
UTI_ZeroTimespec(tx_ts);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
||||
{
|
||||
NtpTimestamps *tss;
|
||||
uint32_t i, index;
|
||||
uint64_t rx;
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
/* Allocate the array on first use */
|
||||
if (!ntp_ts_map.timestamps) {
|
||||
ntp_ts_map.timestamps = ARR_CreateInstance(sizeof (NtpTimestamps));
|
||||
ARR_SetSize(ntp_ts_map.timestamps, ntp_ts_map.max_size);
|
||||
}
|
||||
|
||||
rx = ntp64_to_int64(rx_ts);
|
||||
|
||||
if (rx == 0ULL)
|
||||
return;
|
||||
|
||||
/* Disable the RX timestamp if it already exists to avoid responding
|
||||
with a wrong TX timestamp */
|
||||
if (find_ntp_rx_ts(rx, &index)) {
|
||||
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(index <= ntp_ts_map.size);
|
||||
|
||||
if (index == ntp_ts_map.size) {
|
||||
/* Increase the size or drop the oldest timestamp to make room for
|
||||
the new timestamp */
|
||||
index = push_ntp_tss(index);
|
||||
} else {
|
||||
/* Trim timestamps in distant future after backward step */
|
||||
while (index < ntp_ts_map.size &&
|
||||
get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - rx > NTPTS_FUTURE_LIMIT)
|
||||
ntp_ts_map.size--;
|
||||
|
||||
/* Insert the timestamp if it is close to the latest timestamp.
|
||||
Otherwise, replace the closest older or the oldest timestamp. */
|
||||
if (index + NTPTS_INSERT_LIMIT >= ntp_ts_map.size) {
|
||||
index = push_ntp_tss(index);
|
||||
for (i = ntp_ts_map.size - 1; i > index; i--)
|
||||
*get_ntp_tss(i) = *get_ntp_tss(i - 1);
|
||||
} else {
|
||||
if (index > 0)
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
ntp_ts_map.cached_index = index;
|
||||
ntp_ts_map.cached_rx_ts = rx;
|
||||
|
||||
tss = get_ntp_tss(index);
|
||||
tss->rx_ts = rx;
|
||||
tss->flags = 0;
|
||||
set_ntp_tx_offset(tss, rx_ts, tx_ts);
|
||||
|
||||
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
|
||||
index, ntp_ts_map.first, ntp_ts_map.size);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
if (!ntp_ts_map.timestamps)
|
||||
return;
|
||||
|
||||
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||
return;
|
||||
|
||||
set_ntp_tx_offset(get_ntp_tss(index), rx_ts, tx_ts);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CLG_GetNtpMinPoll(void)
|
||||
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
||||
{
|
||||
return limit_interval[CLG_NTP];
|
||||
NtpTimestamps *tss;
|
||||
uint32_t index;
|
||||
|
||||
if (!ntp_ts_map.timestamps)
|
||||
return 0;
|
||||
|
||||
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||
return 0;
|
||||
|
||||
tss = get_ntp_tss(index);
|
||||
|
||||
if (tss->flags & NTPTS_DISABLED)
|
||||
return 0;
|
||||
|
||||
get_ntp_tx(tss, tx_ts);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
CLG_DisableNtpTimestamps(NTP_int64 *rx_ts)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
if (!ntp_ts_map.timestamps)
|
||||
return;
|
||||
|
||||
if (find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
|
@ -43,9 +43,14 @@ extern int CLG_GetClientIndex(IPAddr *client);
|
|||
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
|
||||
extern int CLG_LimitServiceRate(CLG_Service service, int index);
|
||||
extern void CLG_LogAuthNtpRequest(void);
|
||||
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
|
||||
extern int CLG_GetNtpMinPoll(void);
|
||||
|
||||
/* Functions to save and retrieve timestamps for server interleaved mode */
|
||||
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
||||
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
||||
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
||||
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
|
||||
|
||||
/* And some reporting functions, for use by chronyc. */
|
||||
|
||||
extern int CLG_GetNumberOfIndices(void);
|
||||
|
|
40
ntp_core.c
40
ntp_core.c
|
@ -2042,8 +2042,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||
{
|
||||
NTP_PacketInfo info;
|
||||
NTP_Mode my_mode;
|
||||
NTP_int64 *local_ntp_rx, *local_ntp_tx;
|
||||
NTP_Local_Timestamp local_tx, *tx_ts;
|
||||
NTP_int64 ntp_rx, *local_ntp_rx;
|
||||
int log_index, interleaved, poll, version;
|
||||
uint32_t kod;
|
||||
|
||||
|
@ -2106,7 +2106,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||
CLG_LogAuthNtpRequest();
|
||||
}
|
||||
|
||||
local_ntp_rx = local_ntp_tx = NULL;
|
||||
local_ntp_rx = NULL;
|
||||
tx_ts = NULL;
|
||||
interleaved = 0;
|
||||
|
||||
|
@ -2115,18 +2115,15 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||
in the interleaved mode. This means the third reply to a new client is
|
||||
the earliest one that can be interleaved. We don't want to waste time
|
||||
on clients that are not using the interleaved mode. */
|
||||
if (kod == 0 && log_index >= 0) {
|
||||
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
|
||||
interleaved = !UTI_IsZeroNtp64(local_ntp_rx) &&
|
||||
!UTI_CompareNtp64(&message->originate_ts, local_ntp_rx) &&
|
||||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts);
|
||||
if (kod == 0 &&
|
||||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) != 0) {
|
||||
ntp_rx = message->originate_ts;
|
||||
local_ntp_rx = &ntp_rx;
|
||||
interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts);
|
||||
|
||||
if (interleaved) {
|
||||
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
|
||||
tx_ts = &local_tx;
|
||||
} else {
|
||||
UTI_ZeroNtp64(local_ntp_tx);
|
||||
local_ntp_tx = NULL;
|
||||
CLG_DisableNtpTimestamps(&ntp_rx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2144,9 +2141,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr,
|
||||
message, &info);
|
||||
|
||||
/* Save the transmit timestamp */
|
||||
if (tx_ts)
|
||||
UTI_TimespecToNtp64(&tx_ts->ts, local_ntp_tx, NULL);
|
||||
if (local_ntp_rx)
|
||||
CLG_SaveNtpTimestamps(local_ntp_rx, tx_ts ? &tx_ts->ts : NULL);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -2208,10 +2204,9 @@ void
|
|||
NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
||||
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
|
||||
{
|
||||
NTP_int64 *local_ntp_rx, *local_ntp_tx;
|
||||
NTP_Local_Timestamp local_tx;
|
||||
NTP_int64 *local_ntp_rx;
|
||||
NTP_PacketInfo info;
|
||||
int log_index;
|
||||
|
||||
if (!parse_packet(message, length, &info))
|
||||
return;
|
||||
|
@ -2219,18 +2214,17 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||
if (info.mode == MODE_BROADCAST)
|
||||
return;
|
||||
|
||||
log_index = CLG_GetClientIndex(&remote_addr->ip_addr);
|
||||
if (log_index < 0)
|
||||
return;
|
||||
|
||||
if (SMT_IsEnabled() && info.mode == MODE_SERVER)
|
||||
UTI_AddDoubleToTimespec(&tx_ts->ts, SMT_GetOffset(&tx_ts->ts), &tx_ts->ts);
|
||||
|
||||
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
|
||||
local_ntp_rx = &message->receive_ts;
|
||||
|
||||
if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &local_tx.ts))
|
||||
return;
|
||||
|
||||
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
|
||||
update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
|
||||
UTI_TimespecToNtp64(&local_tx.ts, local_ntp_tx, NULL);
|
||||
|
||||
CLG_UpdateNtpTxTimestamp(local_ntp_rx, &local_tx.ts);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
|
@ -8,6 +8,18 @@ client_conf="
|
|||
logdir tmp
|
||||
log rawmeasurements"
|
||||
|
||||
server_conf="noclientlog"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
|
||||
rm -f tmp/measurements.log
|
||||
|
||||
server_conf=""
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
|
|
|
@ -25,15 +25,24 @@
|
|||
|
||||
#include <clientlog.c>
|
||||
|
||||
static uint64_t
|
||||
get_random64(void)
|
||||
{
|
||||
return ((uint64_t)random() << 40) ^ ((uint64_t)random() << 20) ^ random();
|
||||
}
|
||||
|
||||
void
|
||||
test_unit(void)
|
||||
{
|
||||
int i, j, index;
|
||||
uint64_t ts64, prev_first_ts64, prev_last_ts64, max_step;
|
||||
uint32_t index2, prev_first, prev_size;
|
||||
struct timespec ts, ts2;
|
||||
int i, j, k, index, shift;
|
||||
CLG_Service s;
|
||||
struct timespec ts;
|
||||
NTP_int64 ntp_ts;
|
||||
IPAddr ip;
|
||||
char conf[][100] = {
|
||||
"clientloglimit 10000",
|
||||
"clientloglimit 20000",
|
||||
"ratelimit interval 3 burst 4 leak 3",
|
||||
"cmdratelimit interval 3 burst 4 leak 3",
|
||||
"ntsratelimit interval 6 burst 8 leak 3",
|
||||
|
@ -67,7 +76,7 @@ test_unit(void)
|
|||
}
|
||||
|
||||
DEBUG_LOG("records %u", ARR_GetSize(records));
|
||||
TEST_CHECK(ARR_GetSize(records) == 64);
|
||||
TEST_CHECK(ARR_GetSize(records) == 128);
|
||||
|
||||
s = CLG_NTP;
|
||||
|
||||
|
@ -82,6 +91,158 @@ test_unit(void)
|
|||
DEBUG_LOG("requests %d responses %d", i, j);
|
||||
TEST_CHECK(j * 4 < i && j * 6 > i);
|
||||
|
||||
TEST_CHECK(!ntp_ts_map.timestamps);
|
||||
|
||||
UTI_ZeroNtp64(&ntp_ts);
|
||||
CLG_SaveNtpTimestamps(&ntp_ts, NULL);
|
||||
TEST_CHECK(ntp_ts_map.timestamps);
|
||||
TEST_CHECK(ntp_ts_map.first == 0);
|
||||
TEST_CHECK(ntp_ts_map.size == 0);
|
||||
TEST_CHECK(ntp_ts_map.max_size == 128);
|
||||
TEST_CHECK(ARR_GetSize(ntp_ts_map.timestamps) == ntp_ts_map.max_size);
|
||||
|
||||
TEST_CHECK(ntp_ts_map.max_size > NTPTS_INSERT_LIMIT);
|
||||
|
||||
for (i = 0; i < 200; i++) {
|
||||
DEBUG_LOG("iteration %d", i);
|
||||
|
||||
max_step = (1ULL << (i % 50));
|
||||
ts64 = 0ULL - 100 * max_step;
|
||||
|
||||
ntp_ts_map.first = i % ntp_ts_map.max_size;
|
||||
ntp_ts_map.size = 0;
|
||||
ntp_ts_map.cached_rx_ts = 0ULL;
|
||||
|
||||
for (j = 0; j < 500; j++) {
|
||||
do {
|
||||
ts64 += get_random64() % max_step + 1;
|
||||
} while (ts64 == 0ULL);
|
||||
|
||||
int64_to_ntp64(ts64, &ntp_ts);
|
||||
|
||||
if (random() % 10) {
|
||||
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
|
||||
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
|
||||
} else {
|
||||
UTI_ZeroTimespec(&ts);
|
||||
}
|
||||
|
||||
CLG_SaveNtpTimestamps(&ntp_ts,
|
||||
UTI_IsZeroTimespec(&ts) ? (random() % 2 ? &ts : NULL) : &ts);
|
||||
|
||||
if (j < ntp_ts_map.max_size) {
|
||||
TEST_CHECK(ntp_ts_map.size == j + 1);
|
||||
TEST_CHECK(ntp_ts_map.first == i % ntp_ts_map.max_size);
|
||||
} else {
|
||||
TEST_CHECK(ntp_ts_map.size == ntp_ts_map.max_size);
|
||||
TEST_CHECK(ntp_ts_map.first == (i + j + ntp_ts_map.size + 1) % ntp_ts_map.max_size);
|
||||
}
|
||||
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
|
||||
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
|
||||
|
||||
for (k = random() % 4; k > 0; k--) {
|
||||
int64_to_ntp64(get_ntp_tss(random() % ntp_ts_map.size)->rx_ts, &ntp_ts);
|
||||
if (random() % 2)
|
||||
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
|
||||
|
||||
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
|
||||
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
|
||||
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
|
||||
|
||||
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
|
||||
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
|
||||
|
||||
if (ntp_ts_map.size > 1) {
|
||||
index = random() % (ntp_ts_map.size - 1);
|
||||
if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) {
|
||||
int64_to_ntp64(get_ntp_tss(index)->rx_ts + 1, &ntp_ts);
|
||||
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
|
||||
int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts);
|
||||
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
|
||||
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
|
||||
}
|
||||
}
|
||||
|
||||
if (random() % 2) {
|
||||
int64_to_ntp64(get_ntp_tss(0)->rx_ts - 1, &ntp_ts);
|
||||
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
|
||||
int64_to_ntp64(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts + 1, &ntp_ts);
|
||||
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
|
||||
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 500; j++) {
|
||||
shift = (i % 3) * 26;
|
||||
|
||||
if (i % 7 == 0) {
|
||||
while (ntp_ts_map.size < ntp_ts_map.max_size) {
|
||||
ts64 += get_random64() >> (shift + 8);
|
||||
int64_to_ntp64(ts64, &ntp_ts);
|
||||
CLG_SaveNtpTimestamps(&ntp_ts, NULL);
|
||||
if (ntp_ts_map.cached_index + NTPTS_INSERT_LIMIT < ntp_ts_map.size)
|
||||
ts64 = get_ntp_tss(ntp_ts_map.size - 1)->rx_ts;
|
||||
}
|
||||
}
|
||||
do {
|
||||
if (ntp_ts_map.size > 1 && random() % 2) {
|
||||
k = random() % (ntp_ts_map.size - 1);
|
||||
ts64 = get_ntp_tss(k)->rx_ts +
|
||||
(get_ntp_tss(k + 1)->rx_ts - get_ntp_tss(k)->rx_ts) / 2;
|
||||
} else {
|
||||
ts64 = get_random64() >> shift;
|
||||
}
|
||||
} while (ts64 == 0ULL);
|
||||
|
||||
int64_to_ntp64(ts64, &ntp_ts);
|
||||
|
||||
prev_first = ntp_ts_map.first;
|
||||
prev_size = ntp_ts_map.size;
|
||||
prev_first_ts64 = get_ntp_tss(0)->rx_ts;
|
||||
prev_last_ts64 = get_ntp_tss(prev_size - 1)->rx_ts;
|
||||
CLG_SaveNtpTimestamps(&ntp_ts, NULL);
|
||||
|
||||
TEST_CHECK(find_ntp_rx_ts(ts64, &index2));
|
||||
|
||||
if (ntp_ts_map.size > 1) {
|
||||
TEST_CHECK(ntp_ts_map.size > 0 && ntp_ts_map.size <= ntp_ts_map.max_size);
|
||||
if (get_ntp_tss(index2)->flags & NTPTS_DISABLED)
|
||||
continue;
|
||||
|
||||
TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - ts64 <= NTPTS_FUTURE_LIMIT);
|
||||
|
||||
if ((int64_t)(prev_last_ts64 - ts64) <= NTPTS_FUTURE_LIMIT) {
|
||||
TEST_CHECK(prev_size + 1 >= ntp_ts_map.size);
|
||||
if (index2 + NTPTS_INSERT_LIMIT + 1 >= ntp_ts_map.size &&
|
||||
!(index2 == 0 &&
|
||||
((NTPTS_INSERT_LIMIT == prev_size && (int64_t)(ts64 - prev_first_ts64) > 0) ||
|
||||
(NTPTS_INSERT_LIMIT + 1 == prev_size && (int64_t)(ts64 - prev_first_ts64) < 0))))
|
||||
TEST_CHECK((prev_first + prev_size + 1) % ntp_ts_map.max_size ==
|
||||
(ntp_ts_map.first + ntp_ts_map.size) % ntp_ts_map.max_size);
|
||||
else
|
||||
TEST_CHECK(prev_first + prev_size == ntp_ts_map.first + ntp_ts_map.size);
|
||||
}
|
||||
|
||||
TEST_CHECK((int64_t)(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
|
||||
get_ntp_tss(0)->rx_ts) > 0);
|
||||
for (k = 0; k + 1 < ntp_ts_map.size; k++)
|
||||
TEST_CHECK((int64_t)(get_ntp_tss(k + 1)->rx_ts - get_ntp_tss(k)->rx_ts) > 0);
|
||||
}
|
||||
|
||||
if (random() % 10 == 0) {
|
||||
CLG_DisableNtpTimestamps(&ntp_ts);
|
||||
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
|
||||
}
|
||||
|
||||
for (k = random() % 10; k > 0; k--) {
|
||||
ts64 = get_random64() >> shift;
|
||||
int64_to_ntp64(ts64, &ntp_ts);
|
||||
CLG_GetNtpTxTimestamp(&ntp_ts, &ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLG_Finalise();
|
||||
CNF_Finalise();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue