ntp: separate timestamps for restarting symmetric protocol
Save the local receive and remote transmit timestamp needed for (re)starting the symmetric protocol when no valid reply was received separately from the timestamps that are used for synchronization of the local clock. This extends the interval in which the local NTP state is (partially) protected against replay attacks in order to complete a measurement in the interleaved symmetric mode from [last valid RX, next TX] to [last TX, next TX], i.e. it should be the same as in the basic mode.
This commit is contained in:
parent
c5d8af0285
commit
f0f18a02a7
2 changed files with 90 additions and 34 deletions
86
ntp_core.c
86
ntp_core.c
|
@ -149,14 +149,11 @@ struct NCR_Instance_Record {
|
|||
be used for synchronisation */
|
||||
int valid_timestamps;
|
||||
|
||||
/* Flag indicating the timestamps below were updated since last request */
|
||||
int updated_timestamps;
|
||||
|
||||
/* Receive and transmit timestamps from the last received packet */
|
||||
/* Receive and transmit timestamps from the last valid response */
|
||||
NTP_int64 remote_ntp_rx;
|
||||
NTP_int64 remote_ntp_tx;
|
||||
|
||||
/* Local timestamp when the last packet was received from the
|
||||
/* Local timestamp when the last valid response was received from the
|
||||
source. We have to be prepared to tinker with this if the local
|
||||
clock has its frequency adjusted before we repond. The value we
|
||||
store here is what our own local time was when the same arrived.
|
||||
|
@ -183,6 +180,15 @@ struct NCR_Instance_Record {
|
|||
int prev_local_poll;
|
||||
unsigned int prev_tx_count;
|
||||
|
||||
/* Flag indicating the two timestamps below were updated since the
|
||||
last transmission */
|
||||
int updated_init_timestamps;
|
||||
|
||||
/* Timestamps used for (re)starting the symmetric protocol, when we
|
||||
need to respond to a packet which is not a valid response */
|
||||
NTP_int64 init_remote_ntp_tx;
|
||||
NTP_Local_Timestamp init_local_rx;
|
||||
|
||||
/* The instance record in the main source management module. This
|
||||
performs the statistical analysis on the samples we generate */
|
||||
|
||||
|
@ -652,7 +658,6 @@ NCR_ResetInstance(NCR_Instance instance)
|
|||
|
||||
instance->valid_rx = 0;
|
||||
instance->valid_timestamps = 0;
|
||||
instance->updated_timestamps = 0;
|
||||
UTI_ZeroNtp64(&instance->remote_ntp_rx);
|
||||
UTI_ZeroNtp64(&instance->remote_ntp_tx);
|
||||
UTI_ZeroNtp64(&instance->local_ntp_rx);
|
||||
|
@ -662,6 +667,10 @@ NCR_ResetInstance(NCR_Instance instance)
|
|||
zero_local_timestamp(&instance->prev_local_tx);
|
||||
instance->prev_local_poll = 0;
|
||||
instance->prev_tx_count = 0;
|
||||
|
||||
instance->updated_init_timestamps = 0;
|
||||
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
|
||||
zero_local_timestamp(&instance->init_local_rx);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -914,8 +923,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
|
|||
version = NTP_VERSION;
|
||||
}
|
||||
|
||||
/* Allow interleaved mode only if there was a prior transmission */
|
||||
if (interleaved && (!local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
|
||||
/* Check if the packet can be formed in the interleaved mode */
|
||||
if (interleaved && (!remote_ntp_rx || !local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
|
||||
interleaved = 0;
|
||||
|
||||
smooth_time = 0;
|
||||
|
@ -1084,7 +1093,7 @@ transmit_timeout(void *arg)
|
|||
{
|
||||
NCR_Instance inst = (NCR_Instance) arg;
|
||||
NTP_Local_Address local_addr;
|
||||
int interleaved, sent;
|
||||
int interleaved, initial, sent;
|
||||
|
||||
inst->tx_timeout_id = 0;
|
||||
|
||||
|
@ -1137,6 +1146,19 @@ transmit_timeout(void *arg)
|
|||
(inst->mode == MODE_ACTIVE &&
|
||||
inst->prev_tx_count == 1 && inst->tx_count == 0));
|
||||
|
||||
/* In symmetric mode, if no valid response was received since the previous
|
||||
transmission, respond to the last received packet even if it failed some
|
||||
specific NTP tests. This is necessary for starting and restarting the
|
||||
protocol, e.g. when a packet was lost. */
|
||||
initial = inst->mode == MODE_ACTIVE && !inst->valid_rx &&
|
||||
!UTI_IsZeroNtp64(&inst->init_remote_ntp_tx);
|
||||
|
||||
/* Prepare for the response */
|
||||
inst->valid_rx = 0;
|
||||
inst->updated_init_timestamps = 0;
|
||||
if (initial)
|
||||
inst->valid_timestamps = 0;
|
||||
|
||||
/* Check whether we need to 'warm up' the link to the other end by
|
||||
sending an NTP exchange to ensure both ends' ARP caches are
|
||||
primed or whether we need to send two packets first to ensure a
|
||||
|
@ -1148,18 +1170,16 @@ transmit_timeout(void *arg)
|
|||
inst->presend_done--;
|
||||
}
|
||||
|
||||
sent = transmit_packet(inst->mode, interleaved, inst->local_poll,
|
||||
inst->version,
|
||||
/* Send the request (which may also be a response in the symmetric mode) */
|
||||
sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version,
|
||||
inst->auth_mode, inst->auth_key_id,
|
||||
&inst->remote_ntp_rx, &inst->remote_ntp_tx,
|
||||
&inst->local_rx, &inst->local_tx,
|
||||
&inst->local_ntp_rx, &inst->local_ntp_tx,
|
||||
&inst->remote_addr,
|
||||
&local_addr);
|
||||
initial ? NULL : &inst->remote_ntp_rx,
|
||||
initial ? &inst->init_remote_ntp_tx : &inst->remote_ntp_tx,
|
||||
initial ? &inst->init_local_rx : &inst->local_rx,
|
||||
&inst->local_tx, &inst->local_ntp_rx, &inst->local_ntp_tx,
|
||||
&inst->remote_addr, &local_addr);
|
||||
|
||||
++inst->tx_count;
|
||||
inst->valid_rx = 0;
|
||||
inst->updated_timestamps = 0;
|
||||
if (sent)
|
||||
inst->report.total_tx_count++;
|
||||
|
||||
|
@ -1442,6 +1462,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
NTP_Local_Timestamp local_receive, local_transmit;
|
||||
double remote_interval, local_interval, response_time;
|
||||
double delay_time, precision;
|
||||
int updated_timestamps;
|
||||
|
||||
/* ==================== */
|
||||
|
||||
|
@ -1644,21 +1665,34 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
The authentication test (test5) is required to prevent DoS attacks using
|
||||
unauthenticated packets on authenticated symmetric associations. */
|
||||
if ((inst->mode == MODE_CLIENT && valid_packet && !inst->valid_rx) ||
|
||||
(inst->mode == MODE_ACTIVE && (valid_packet || !inst->valid_rx) &&
|
||||
test5 && !UTI_IsZeroNtp64(&message->transmit_ts) &&
|
||||
(!inst->updated_timestamps || (valid_packet && !inst->valid_rx) ||
|
||||
(inst->mode == MODE_ACTIVE && valid_packet &&
|
||||
(!inst->valid_rx ||
|
||||
UTI_CompareNtp64(&inst->remote_ntp_tx, &message->transmit_ts) < 0))) {
|
||||
inst->remote_ntp_rx = message->receive_ts;
|
||||
inst->remote_ntp_tx = message->transmit_ts;
|
||||
inst->local_rx = *rx_ts;
|
||||
inst->valid_timestamps = synced_packet;
|
||||
inst->updated_timestamps = 1;
|
||||
|
||||
UTI_ZeroNtp64(&inst->init_remote_ntp_tx);
|
||||
zero_local_timestamp(&inst->init_local_rx);
|
||||
inst->updated_init_timestamps = 0;
|
||||
updated_timestamps = 2;
|
||||
|
||||
/* Don't use the same set of timestamps for the next sample */
|
||||
if (interleaved_packet)
|
||||
inst->prev_local_tx = inst->local_tx;
|
||||
else
|
||||
zero_local_timestamp(&inst->prev_local_tx);
|
||||
} else if (inst->mode == MODE_ACTIVE &&
|
||||
test1 && !UTI_IsZeroNtp64(&message->transmit_ts) && test5 &&
|
||||
(!inst->updated_init_timestamps ||
|
||||
UTI_CompareNtp64(&inst->init_remote_ntp_tx, &message->transmit_ts) < 0)) {
|
||||
inst->init_remote_ntp_tx = message->transmit_ts;
|
||||
inst->init_local_rx = *rx_ts;
|
||||
inst->updated_init_timestamps = 1;
|
||||
updated_timestamps = 1;
|
||||
} else {
|
||||
updated_timestamps = 0;
|
||||
}
|
||||
|
||||
/* Accept at most one response per request. The NTP specification recommends
|
||||
|
@ -1694,10 +1728,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
|
||||
remote_interval, local_interval, response_time,
|
||||
tss_chars[local_transmit.source], tss_chars[local_receive.source]);
|
||||
DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d presend=%d valid=%d good=%d updated=%d",
|
||||
DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d"
|
||||
" presend=%d valid=%d good=%d updated=%d",
|
||||
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
|
||||
kod_rate, interleaved_packet, inst->presend_done, valid_packet, good_packet,
|
||||
!UTI_CompareTimespecs(&inst->local_rx.ts, &rx_ts->ts));
|
||||
updated_timestamps);
|
||||
|
||||
if (valid_packet) {
|
||||
inst->remote_poll = message->poll;
|
||||
|
@ -2189,6 +2224,9 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
|
|||
if (!UTI_IsZeroTimespec(&inst->prev_local_tx.ts))
|
||||
UTI_AdjustTimespec(&inst->prev_local_tx.ts, when, &inst->prev_local_tx.ts, &delta, dfreq,
|
||||
doffset);
|
||||
if (!UTI_IsZeroTimespec(&inst->init_local_rx.ts))
|
||||
UTI_AdjustTimespec(&inst->init_local_rx.ts, when, &inst->local_rx.ts, &delta, dfreq,
|
||||
doffset);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
|
@ -74,7 +74,6 @@ send_request(void)
|
|||
|
||||
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);
|
||||
|
@ -165,13 +164,13 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
|
|||
}
|
||||
|
||||
static void
|
||||
process_response(int valid, int updated)
|
||||
process_response(int valid, int updated_sync, int updated_init)
|
||||
{
|
||||
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;
|
||||
struct timespec prev_rx_ts, prev_init_rx_ts;
|
||||
int prev_open_socket;
|
||||
|
||||
res = &res_buffer.ntp_pkt;
|
||||
|
@ -186,6 +185,7 @@ process_response(int valid, int updated)
|
|||
prev_rx_count = inst->report.total_rx_count;
|
||||
prev_valid_count = inst->report.total_valid_count;
|
||||
prev_rx_ts = inst->local_rx.ts;
|
||||
prev_init_rx_ts = inst->init_local_rx.ts;
|
||||
prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD;
|
||||
|
||||
NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
|
||||
|
@ -200,10 +200,20 @@ process_response(int valid, int updated)
|
|||
else
|
||||
TEST_CHECK(prev_valid_count == inst->report.total_valid_count);
|
||||
|
||||
if (updated)
|
||||
if (updated_sync)
|
||||
TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
|
||||
else
|
||||
TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
|
||||
|
||||
if (updated_init)
|
||||
TEST_CHECK(UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
|
||||
else
|
||||
TEST_CHECK(!UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
|
||||
|
||||
if (valid) {
|
||||
TEST_CHECK(UTI_IsZeroTimespec(&inst->init_local_rx.ts));
|
||||
TEST_CHECK(UTI_IsZeroNtp64(&inst->init_remote_ntp_tx));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -259,30 +269,38 @@ test_unit(void)
|
|||
updated = (valid || inst->mode == MODE_ACTIVE) &&
|
||||
(!source.params.authkey || authenticated);
|
||||
has_updated = has_updated || updated;
|
||||
if (inst->mode == MODE_CLIENT)
|
||||
updated = 0;
|
||||
|
||||
send_request();
|
||||
|
||||
send_response(interleaved, authenticated, 1, 0, 1);
|
||||
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
|
||||
DEBUG_LOG("response 1");
|
||||
process_response(0, 0, updated);
|
||||
|
||||
if (source.params.authkey) {
|
||||
send_response(interleaved, authenticated, 1, 1, 0);
|
||||
process_response(0, 0);
|
||||
DEBUG_LOG("response 2");
|
||||
process_response(0, 0, 0);
|
||||
}
|
||||
|
||||
send_response(interleaved, authenticated, 1, 1, 1);
|
||||
process_response(valid, updated);
|
||||
process_response(0, 0);
|
||||
DEBUG_LOG("response 3");
|
||||
process_response(valid, valid, updated);
|
||||
DEBUG_LOG("response 4");
|
||||
process_response(0, 0, 0);
|
||||
|
||||
advance_time(-1.0);
|
||||
|
||||
send_response(interleaved, authenticated, 1, 1, 1);
|
||||
process_response(0, 0);
|
||||
DEBUG_LOG("response 5");
|
||||
process_response(0, 0, updated && valid);
|
||||
|
||||
advance_time(1.0);
|
||||
|
||||
send_response(interleaved, authenticated, 1, 1, 1);
|
||||
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
|
||||
DEBUG_LOG("response 6");
|
||||
process_response(0, valid && updated, updated);
|
||||
}
|
||||
|
||||
NCR_DestroyInstance(inst);
|
||||
|
|
Loading…
Reference in a new issue