ntp: add client support for experimental extension field
Add "extfield F323" option to include the new extension field in requests. If the server responds with this field, use the root delay/dispersion and monotonic timestamp. Accumulate changes in the offset between the monotonic and real-time receive timestamps and use it for the correction of previous offsets in sourcestats. In the interleaved mode, cancel out the latest change in the offset in timestamps of the previous request and response, which were captured before the change actually happened.
This commit is contained in:
parent
72f7d09f58
commit
2319f72b29
7 changed files with 108 additions and 7 deletions
1
candm.h
1
candm.h
|
@ -270,6 +270,7 @@ typedef struct {
|
|||
#define REQ_ADDSRC_BURST 0x100
|
||||
#define REQ_ADDSRC_NTS 0x200
|
||||
#define REQ_ADDSRC_COPY 0x400
|
||||
#define REQ_ADDSRC_EF_EXP1 0x800
|
||||
|
||||
typedef struct {
|
||||
uint32_t type;
|
||||
|
|
1
client.c
1
client.c
|
@ -942,6 +942,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
|
|||
(data.params.burst ? REQ_ADDSRC_BURST : 0) |
|
||||
(data.params.nts ? REQ_ADDSRC_NTS : 0) |
|
||||
(data.params.copy ? REQ_ADDSRC_COPY : 0) |
|
||||
(data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) |
|
||||
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
|
||||
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
|
||||
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
|
||||
|
|
2
cmdmon.c
2
cmdmon.c
|
@ -768,6 +768,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
|
||||
params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
|
||||
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
|
||||
params.ext_fields =
|
||||
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
|
||||
params.sel_options =
|
||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
|
||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
|
||||
|
|
12
cmdparse.c
12
cmdparse.c
|
@ -43,6 +43,7 @@ int
|
|||
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
{
|
||||
char *hostname, *cmd;
|
||||
uint32_t ef_type;
|
||||
int n;
|
||||
|
||||
src->port = SRC_DEFAULT_PORT;
|
||||
|
@ -65,6 +66,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||
src->params.nts = 0;
|
||||
src->params.nts_port = SRC_DEFAULT_NTSPORT;
|
||||
src->params.copy = 0;
|
||||
src->params.ext_fields = 0;
|
||||
src->params.authkey = INACTIVE_AUTHKEY;
|
||||
src->params.cert_set = SRC_DEFAULT_CERTSET;
|
||||
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
|
||||
|
@ -116,6 +118,16 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||
} else if (!strcasecmp(cmd, "asymmetry")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "extfield")) {
|
||||
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
|
||||
return 0;
|
||||
switch (ef_type) {
|
||||
case NTP_EF_EXP1:
|
||||
src->params.ext_fields |= NTP_EF_FLAG_EXP1;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
} else if (!strcasecmp(cmd, "filter")) {
|
||||
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
|
||||
return 0;
|
||||
|
|
|
@ -287,6 +287,25 @@ This is useful when multiple instances of `chronyd` are running on one computer
|
|||
to synchronise the system clock and other instances started with the *-x*
|
||||
option to operate as NTP servers for other computers with their NTP clocks
|
||||
synchronised to the first instance.
|
||||
*extfield* _type_:::
|
||||
This option enables an NTPv4 extension field specified by its type as a
|
||||
hexadecimal number. It will be included in requests sent to the server and
|
||||
processed in received responses if the server supports it. Note that some
|
||||
server implementations do not respond to requests containing an unknown
|
||||
extension field (*chronyd* as a server responded to such requests since
|
||||
version 2.0).
|
||||
+
|
||||
The following extension field can be enabled by this option:
|
||||
+
|
||||
_F323_::::
|
||||
This is an experimental extension field for some improvements that were
|
||||
proposed for the next version of the NTP protocol (NTPv5). The field contains
|
||||
root delay and dispersion in higher resolution and a monotonic receive
|
||||
timestamp, which enables a frequency transfer between the server and client. It
|
||||
can significantly improve stability of the synchronization. Generally, it
|
||||
should be expected to work only between servers and clients running the same
|
||||
version of *chronyd*.
|
||||
{blank}:::
|
||||
|
||||
[[pool]]*pool* _name_ [_option_]...::
|
||||
The syntax of this directive is similar to that for the <<server,*server*>>
|
||||
|
|
75
ntp_core.c
75
ntp_core.c
|
@ -134,6 +134,10 @@ struct NCR_Instance_Record {
|
|||
|
||||
int ext_field_flags; /* Enabled extension fields */
|
||||
|
||||
uint32_t remote_mono_epoch; /* ID of the source's monotonic scale */
|
||||
double mono_doffset; /* Accumulated offset between source's
|
||||
real-time and monotonic scales */
|
||||
|
||||
NAU_Instance auth; /* Authentication */
|
||||
|
||||
/* Count of transmitted packets since last valid response */
|
||||
|
@ -147,6 +151,7 @@ struct NCR_Instance_Record {
|
|||
int valid_timestamps;
|
||||
|
||||
/* Receive and transmit timestamps from the last valid response */
|
||||
NTP_int64 remote_ntp_monorx;
|
||||
NTP_int64 remote_ntp_rx;
|
||||
NTP_int64 remote_ntp_tx;
|
||||
|
||||
|
@ -283,6 +288,9 @@ static ARR_Instance broadcasts;
|
|||
interleaved mode to prefer a sample using previous timestamps */
|
||||
#define MAX_INTERLEAVED_L2L_RATIO 0.1
|
||||
|
||||
/* Maximum acceptable change in server mono<->real offset */
|
||||
#define MAX_MONO_DOFFSET 16.0
|
||||
|
||||
/* Invalid socket, different from the one in ntp_io.c */
|
||||
#define INVALID_SOCK_FD -2
|
||||
|
||||
|
@ -593,7 +601,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
|||
result->auto_offline = params->auto_offline;
|
||||
result->copy = params->copy && result->mode == MODE_CLIENT;
|
||||
result->poll_target = params->poll_target;
|
||||
result->ext_field_flags = 0;
|
||||
result->ext_field_flags = params->ext_fields;
|
||||
|
||||
if (params->nts) {
|
||||
IPSockAddr nts_address;
|
||||
|
@ -699,9 +707,12 @@ NCR_ResetInstance(NCR_Instance instance)
|
|||
instance->remote_stratum = 0;
|
||||
instance->remote_root_delay = 0.0;
|
||||
instance->remote_root_dispersion = 0.0;
|
||||
instance->remote_mono_epoch = 0;
|
||||
instance->mono_doffset = 0.0;
|
||||
|
||||
instance->valid_rx = 0;
|
||||
instance->valid_timestamps = 0;
|
||||
UTI_ZeroNtp64(&instance->remote_ntp_monorx);
|
||||
UTI_ZeroNtp64(&instance->remote_ntp_rx);
|
||||
UTI_ZeroNtp64(&instance->remote_ntp_tx);
|
||||
UTI_ZeroNtp64(&instance->local_ntp_rx);
|
||||
|
@ -1641,6 +1652,12 @@ process_sample(NCR_Instance inst, NTP_Sample *sample)
|
|||
|
||||
error_in_estimate = fabs(-sample->offset - estimated_offset);
|
||||
|
||||
if (inst->mono_doffset != 0.0) {
|
||||
DEBUG_LOG("Monotonic correction offset=%.9f", inst->mono_doffset);
|
||||
SST_CorrectOffset(SRC_GetSourcestats(inst->source), inst->mono_doffset);
|
||||
inst->mono_doffset = 0.0;
|
||||
}
|
||||
|
||||
SRC_AccumulateSample(inst->source, sample);
|
||||
SRC_SelectSource(inst->source);
|
||||
|
||||
|
@ -1679,16 +1696,19 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
/* Extension fields */
|
||||
int parsed, ef_length, ef_type, ef_body_length;
|
||||
void *ef_body;
|
||||
NTP_ExtFieldExp1 *ef_exp1;
|
||||
|
||||
NTP_Local_Timestamp local_receive, local_transmit;
|
||||
double remote_interval, local_interval, response_time;
|
||||
double delay_time, precision;
|
||||
double delay_time, precision, mono_doffset;
|
||||
int updated_timestamps;
|
||||
|
||||
/* ==================== */
|
||||
|
||||
stats = SRC_GetSourcestats(inst->source);
|
||||
|
||||
ef_exp1 = NULL;
|
||||
|
||||
/* Find requested non-authentication extension fields */
|
||||
if (inst->ext_field_flags & info->ext_field_flags) {
|
||||
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
|
||||
|
@ -1697,6 +1717,10 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
break;
|
||||
|
||||
switch (ef_type) {
|
||||
case NTP_EF_EXP1:
|
||||
if (inst->ext_field_flags & NTP_EF_FLAG_EXP1 && ef_body_length == sizeof (*ef_exp1))
|
||||
ef_exp1 = ef_body;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1704,8 +1728,13 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
pkt_leap = NTP_LVM_TO_LEAP(message->lvm);
|
||||
pkt_version = NTP_LVM_TO_VERSION(message->lvm);
|
||||
pkt_refid = ntohl(message->reference_id);
|
||||
if (ef_exp1) {
|
||||
pkt_root_delay = UTI_Ntp32f28ToDouble(ef_exp1->root_delay);
|
||||
pkt_root_dispersion = UTI_Ntp32f28ToDouble(ef_exp1->root_dispersion);
|
||||
} else {
|
||||
pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
|
||||
pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
|
||||
}
|
||||
|
||||
/* Check if the packet is valid per RFC 5905, section 8.
|
||||
The test values are 1 when passed and 0 when failed. */
|
||||
|
@ -1762,6 +1791,26 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
struct timespec local_average, remote_average, prev_remote_transmit;
|
||||
double prev_remote_poll_interval, root_delay, root_dispersion;
|
||||
|
||||
/* If the remote monotonic timestamps are available and are from the same
|
||||
epoch, calculate the change in the offset between the monotonic and
|
||||
real-time clocks, i.e. separate the source's time corrections from
|
||||
frequency corrections. The offset is accumulated between measurements.
|
||||
It will correct old measurements kept in sourcestats before accumulating
|
||||
the new sample. In the interleaved mode, cancel the correction out in
|
||||
remote timestamps of the previous request and response, which were
|
||||
captured before the source accumulated the new time corrections. */
|
||||
if (ef_exp1 && inst->remote_mono_epoch == ntohl(ef_exp1->mono_epoch) &&
|
||||
!UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts) &&
|
||||
!UTI_IsZeroNtp64(&inst->remote_ntp_monorx)) {
|
||||
mono_doffset =
|
||||
UTI_DiffNtp64ToDouble(&ef_exp1->mono_receive_ts, &inst->remote_ntp_monorx) -
|
||||
UTI_DiffNtp64ToDouble(&message->receive_ts, &inst->remote_ntp_rx);
|
||||
if (fabs(mono_doffset) > MAX_MONO_DOFFSET)
|
||||
mono_doffset = 0.0;
|
||||
} else {
|
||||
mono_doffset = 0.0;
|
||||
}
|
||||
|
||||
/* Select remote and local timestamps for the new sample */
|
||||
if (interleaved_packet) {
|
||||
/* Prefer previous local TX and remote RX timestamps if it will make
|
||||
|
@ -1772,6 +1821,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
UTI_DiffTimespecsToDouble(&inst->local_tx.ts, &inst->local_rx.ts) >
|
||||
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->prev_local_tx.ts)) {
|
||||
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_receive);
|
||||
UTI_AddDoubleToTimespec(&remote_receive, -mono_doffset, &remote_receive);
|
||||
remote_request_receive = remote_receive;
|
||||
local_transmit = inst->prev_local_tx;
|
||||
root_delay = inst->remote_root_delay;
|
||||
|
@ -1784,6 +1834,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
root_dispersion = MAX(pkt_root_dispersion, inst->remote_root_dispersion);
|
||||
}
|
||||
UTI_Ntp64ToTimespec(&message->transmit_ts, &remote_transmit);
|
||||
UTI_AddDoubleToTimespec(&remote_transmit, -mono_doffset, &remote_transmit);
|
||||
UTI_Ntp64ToTimespec(&inst->remote_ntp_tx, &prev_remote_transmit);
|
||||
local_receive = inst->local_rx;
|
||||
} else {
|
||||
|
@ -1879,6 +1930,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
sample.offset = sample.peer_delay = sample.peer_dispersion = 0.0;
|
||||
sample.root_delay = sample.root_dispersion = 0.0;
|
||||
sample.time = rx_ts->ts;
|
||||
mono_doffset = 0.0;
|
||||
local_receive = *rx_ts;
|
||||
local_transmit = inst->local_tx;
|
||||
testA = testB = testC = testD = 0;
|
||||
|
@ -1909,6 +1961,19 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
inst->updated_init_timestamps = 0;
|
||||
updated_timestamps = 2;
|
||||
|
||||
/* If available, update the monotonic timestamp and accumulate the offset.
|
||||
This needs to be done here to no lose changes in remote_ntp_rx in
|
||||
symmetric mode when there are multiple responses per request. */
|
||||
if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) {
|
||||
inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch);
|
||||
inst->remote_ntp_monorx = ef_exp1->mono_receive_ts;
|
||||
inst->mono_doffset += mono_doffset;
|
||||
} else {
|
||||
inst->remote_mono_epoch = 0;
|
||||
UTI_ZeroNtp64(&inst->remote_ntp_monorx);
|
||||
inst->mono_doffset = 0.0;
|
||||
}
|
||||
|
||||
/* Don't use the same set of timestamps for the next sample */
|
||||
if (interleaved_packet)
|
||||
inst->prev_local_tx = inst->local_tx;
|
||||
|
@ -1945,7 +2010,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
(unsigned int)local_transmit.source >= sizeof (tss_chars))
|
||||
assert(0);
|
||||
|
||||
DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%f root_disp=%f refid=%"PRIx32" [%s]",
|
||||
DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%.9f root_disp=%.9f refid=%"PRIx32" [%s]",
|
||||
message->lvm, message->stratum, message->poll, message->precision,
|
||||
pkt_root_delay, pkt_root_dispersion, pkt_refid,
|
||||
message->stratum == NTP_INVALID_STRATUM || message->stratum == 1 ?
|
||||
|
@ -1958,8 +2023,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||
DEBUG_LOG("offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f",
|
||||
sample.offset, sample.peer_delay, sample.peer_dispersion,
|
||||
sample.root_delay, sample.root_dispersion);
|
||||
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
|
||||
remote_interval, local_interval, response_time,
|
||||
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f mono_doffset=%.9f txs=%c rxs=%c",
|
||||
remote_interval, local_interval, response_time, mono_doffset,
|
||||
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",
|
||||
|
|
|
@ -55,6 +55,7 @@ typedef struct {
|
|||
int nts;
|
||||
int nts_port;
|
||||
int copy;
|
||||
int ext_fields;
|
||||
uint32_t authkey;
|
||||
uint32_t cert_set;
|
||||
double max_delay;
|
||||
|
|
Loading…
Reference in a new issue