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 */
/* 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];
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;
}
/* 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++)
;
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;
}
/* ================================================== */
return;
ARR_DestroyInstance(records);
+ if (ntp_ts_map.timestamps)
+ ARR_DestroyInstance(ntp_ts_map.timestamps);
}
/* ================================================== */
/* ================================================== */
-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);
+/* ================================================== */
+
+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--;
+ }
- *rx_ts = &record->ntp_rx_ts;
- *tx_ts = &record->ntp_tx_ts;
+ 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;
}
/* ================================================== */
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);
{
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;
CLG_LogAuthNtpRequest();
}
- local_ntp_rx = local_ntp_tx = NULL;
+ local_ntp_rx = NULL;
tx_ts = NULL;
interleaved = 0;
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);
}
}
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);
}
/* ================================================== */
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;
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);
}
/* ================================================== */
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
#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",
}
DEBUG_LOG("records %u", ARR_GetSize(records));
- TEST_CHECK(ARR_GetSize(records) == 64);
+ TEST_CHECK(ARR_GetSize(records) == 128);
s = CLG_NTP;
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();
}