diff --git a/clientlog.c b/clientlog.c index 7f71bfe..83dc693 100644 --- a/clientlog.c +++ b/clientlog.c @@ -39,6 +39,7 @@ #include "clientlog.h" #include "conf.h" #include "memory.h" +#include "ntp.h" #include "reports.h" #include "util.h" #include "logging.h" @@ -57,6 +58,8 @@ typedef struct { int8_t cmd_rate; int8_t ntp_timeout_rate; uint8_t 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 */ @@ -206,6 +209,8 @@ get_record(IPAddr *ip) record->ntp_rate = record->cmd_rate = INVALID_RATE; record->ntp_timeout_rate = INVALID_RATE; record->flags = 0; + record->ntp_rx_ts.hi = record->ntp_rx_ts.lo = 0; + record->ntp_tx_ts.hi = record->ntp_tx_ts.lo = 0; return record; } @@ -405,6 +410,23 @@ get_index(Record *record) /* ================================================== */ +int +CLG_GetClientIndex(IPAddr *client) +{ + Record *record; + + if (!active) + return -1; + + record = get_record(client); + if (record == NULL) + return -1; + + return get_index(record); +} + +/* ================================================== */ + int CLG_LogNTPAccess(IPAddr *client, struct timespec *now) { @@ -553,7 +575,19 @@ CLG_LimitCommandResponseRate(int index) /* ================================================== */ -extern int +void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts) +{ + Record *record; + + record = ARR_GetElement(records, index); + + *rx_ts = &record->ntp_rx_ts; + *tx_ts = &record->ntp_tx_ts; +} + +/* ================================================== */ + +int CLG_GetNumberOfIndices(void) { if (!active) diff --git a/clientlog.h b/clientlog.h index ff44b0b..337b4d8 100644 --- a/clientlog.h +++ b/clientlog.h @@ -33,10 +33,12 @@ extern void CLG_Initialise(void); extern void CLG_Finalise(void); +extern int CLG_GetClientIndex(IPAddr *client); extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now); extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now); extern int CLG_LimitNTPResponseRate(int index); extern int CLG_LimitCommandResponseRate(int index); +extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts); /* And some reporting functions, for use by chronyc. */ diff --git a/ntp_core.c b/ntp_core.c index 55d1e98..62fa2d1 100644 --- a/ntp_core.c +++ b/ntp_core.c @@ -471,17 +471,17 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar /* Client socket will be obtained when sending request */ result->local_addr.sock_fd = INVALID_SOCK_FD; result->mode = MODE_CLIENT; - result->interleaved = 0; break; case NTP_PEER: result->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr); result->mode = MODE_ACTIVE; - result->interleaved = params->interleaved; break; default: assert(0); } + result->interleaved = params->interleaved; + result->minpoll = params->minpoll; if (result->minpoll < MIN_POLL) result->minpoll = SRC_DEFAULT_MINPOLL; @@ -1446,10 +1446,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr, /* Update the NTP timestamps. If it's a valid packet from a synchronised source, the timestamps may be used later when processing a packet in the - interleaved mode. The authentication test (test5) is required to prevent - denial-of-service attacks using unauthenticated packets on authenticated - symmetric associations. */ - if (test5) { + interleaved mode. Protect the timestamps against replay attacks in client + mode. 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 && test5)) { inst->remote_ntp_rx = message->receive_ts; inst->remote_ntp_tx = message->transmit_ts; inst->local_rx = *rx_ts; @@ -1738,7 +1739,9 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length) { NTP_Mode pkt_mode, my_mode; - int valid_auth, log_index; + NTP_int64 *local_ntp_rx, *local_ntp_tx; + NTP_Local_Timestamp local_tx, *tx_ts; + int valid_auth, log_index, interleaved; AuthenticationMode auth_mode; uint32_t key_id; @@ -1803,15 +1806,41 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a } } - /* Send a reply. - - copy the poll value as the client may use it to control its polling - interval - - originate timestamp is the client's transmit time - - don't save our transmit timestamp as we aren't maintaining state about - this client */ - transmit_packet(my_mode, 0, message->poll, NTP_LVM_TO_VERSION(message->lvm), + local_ntp_rx = local_ntp_tx = NULL; + tx_ts = NULL; + interleaved = 0; + + /* Check if the client is using the interleaved mode. If it is, save the + new transmit timestamp and if the old transmit timestamp is valid, respond + 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 (log_index >= 0) { + CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx); + interleaved = (local_ntp_rx->hi || local_ntp_rx->lo) && + message->originate_ts.hi == local_ntp_rx->hi && + message->originate_ts.lo == local_ntp_rx->lo; + + if (interleaved) { + if (local_ntp_tx->hi || local_ntp_tx->lo) + UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts); + else + interleaved = 0; + tx_ts = &local_tx; + } else { + local_ntp_tx->hi = local_ntp_tx->lo = 0; + local_ntp_tx = NULL; + } + } + + /* Send a reply */ + transmit_packet(my_mode, interleaved, message->poll, NTP_LVM_TO_VERSION(message->lvm), auth_mode, key_id, &message->receive_ts, &message->transmit_ts, - rx_ts, NULL, NULL, NULL, remote_addr, local_addr); + rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr); + + /* Save the transmit timestamp */ + if (tx_ts) + UTI_TimespecToNtp64(&tx_ts->ts, local_ntp_tx, NULL); } /* ================================================== */ @@ -1877,8 +1906,22 @@ void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length) { - /* Nothing to do yet */ - DEBUG_LOG(LOGF_NtpCore, "Process TX unknown"); + NTP_int64 *local_ntp_rx, *local_ntp_tx; + NTP_Local_Timestamp local_tx; + int log_index; + + if (!check_packet_format(message, length)) + return; + + log_index = CLG_GetClientIndex(&remote_addr->ip_addr); + if (log_index < 0) + return; + + CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx); + + 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); } /* ================================================== */ diff --git a/test/unit/clientlog.c b/test/unit/clientlog.c index 810b84b..ee29936 100644 --- a/test/unit/clientlog.c +++ b/test/unit/clientlog.c @@ -66,7 +66,7 @@ test_unit(void) } DEBUG_LOG(0, "records %d", ARR_GetSize(records)); - TEST_CHECK(ARR_GetSize(records) == 128); + TEST_CHECK(ARR_GetSize(records) == 64); for (i = j = 0; i < 10000; i++) { ts.tv_sec += 1;