]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
ntp: add client support for experimental extension field
authorMiroslav Lichvar <mlichvar@redhat.com>
Mon, 15 Nov 2021 09:08:34 +0000 (10:08 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Tue, 16 Nov 2021 09:34:32 +0000 (10:34 +0100)
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.

candm.h
client.c
cmdmon.c
cmdparse.c
doc/chrony.conf.adoc
ntp_core.c
srcparams.h

diff --git a/candm.h b/candm.h
index 86d8107a97e8b9741edcf4c22c8ea9491565c2d1..dd9941361861665a4179094a3d3482cb58b4aa35 100644 (file)
--- a/candm.h
+++ b/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;
index a9aa6b509efd7ec6ae8bd58daca52cae13b39640..ed551c6db9a675b930073e27ead3bc45453cd5f0 100644 (file)
--- a/client.c
+++ b/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) |
index fee398b44cb34a62d84179c55135fe986f624fe0..cd064707495c236d0b58b104571b7c58f69afa19 100644 (file)
--- a/cmdmon.c
+++ b/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) |
index d44b426c0c70aa4c8f6c59d51621ca3b0abac0cc..f9fe68e70a03ffe5124075c8d0104da274c95611 100644 (file)
@@ -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;
index d76d867bc43a96bbed9406808133145627c4d4ac..6f734d2cb3570f550bcb6e9846b21767c99386dc 100644 (file)
@@ -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*>>
index bb92af5e3a32c6ebba09ce5ba5c32ed5bd8b8938..cda4c9c981fc40d2d2af2a14fefb457fd416cc22 100644 (file)
@@ -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);
-  pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
-  pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
+  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",
index 6211ec791f6cd72e52591501b52f49df4519d7a6..f81114602b37a7978e1173c7152c93f067343ac4 100644 (file)
@@ -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;