diff --git a/candm.h b/candm.h index 52edf29..fe4d5b4 100644 --- a/candm.h +++ b/candm.h @@ -269,6 +269,7 @@ typedef struct { #define REQ_ADDSRC_INTERLEAVED 0x80 #define REQ_ADDSRC_BURST 0x100 #define REQ_ADDSRC_NTS 0x200 +#define REQ_ADDSRC_COPY 0x400 typedef struct { uint32_t type; diff --git a/client.c b/client.c index c0e9022..17e4577 100644 --- a/client.c +++ b/client.c @@ -1098,6 +1098,7 @@ process_cmd_add_source(CMD_Request *msg, char *line) (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) | (data.params.burst ? REQ_ADDSRC_BURST : 0) | (data.params.nts ? REQ_ADDSRC_NTS : 0) | + (data.params.copy ? REQ_ADDSRC_COPY : 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) | diff --git a/cmdmon.c b/cmdmon.c index 3d17cb7..7ee8c12 100644 --- a/cmdmon.c +++ b/cmdmon.c @@ -767,6 +767,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message) params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0; 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.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) | diff --git a/cmdparse.c b/cmdparse.c index 95d8953..e2c5115 100644 --- a/cmdparse.c +++ b/cmdparse.c @@ -64,6 +64,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) src->params.sel_options = 0; src->params.nts = 0; src->params.nts_port = SRC_DEFAULT_NTSPORT; + src->params.copy = 0; src->params.authkey = INACTIVE_AUTHKEY; src->params.cert_set = SRC_DEFAULT_CERTSET; src->params.max_delay = SRC_DEFAULT_MAXDELAY; @@ -91,6 +92,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) src->params.auto_offline = 1; } else if (!strcasecmp(cmd, "burst")) { src->params.burst = 1; + } else if (!strcasecmp(cmd, "copy")) { + src->params.copy = 1; } else if (!strcasecmp(cmd, "iburst")) { src->params.iburst = 1; } else if (!strcasecmp(cmd, "offline")) { diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc index 280278f..ff95ccc 100644 --- a/doc/chrony.conf.adoc +++ b/doc/chrony.conf.adoc @@ -278,6 +278,15 @@ specified by the *key* option and which authentication hash function the key is using. If the output size of the hash function is longer than 160 bits, the default version is 3 for compatibility with older *chronyd* servers. Otherwise, the default version is 4. +*copy*::: +This option specifies that the server and client are closely related, their +configuration does not allow a synchronisation loop to form between them, and +the client is allowed to assume the reference ID and stratum of the server. +This is useful when multiple instances of `chronyd` are running on one computer +(e.g. for security or performance reasons), one primarily operating as a client +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. [[pool]]*pool* _name_ [_option_]...:: The syntax of this directive is similar to that for the <> @@ -320,7 +329,7 @@ address of this host. *chronyd* does not support ephemeral associations. This directive can be used multiple times to specify multiple peers. + The following options of the *server* directive do not work in the *peer* -directive: *iburst*, *burst*, *nts*, *presend*. +directive: *iburst*, *burst*, *nts*, *presend*, *copy*. + When using the *xleave* option, both peers must support and have enabled the interleaved mode, otherwise the synchronisation will work in one direction diff --git a/ntp_core.c b/ntp_core.c index 51c11fd..5470022 100644 --- a/ntp_core.c +++ b/ntp_core.c @@ -107,6 +107,8 @@ struct NCR_Instance_Record { int min_stratum; /* Increase stratum in received packets to the minimum */ + int copy; /* Boolean suppressing own refid and stratum */ + int poll_target; /* Target number of sourcestats samples */ int version; /* Version set in packets for server/peer */ @@ -560,6 +562,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, result->auto_iburst = params->iburst; result->auto_burst = params->burst; result->auto_offline = params->auto_offline; + result->copy = params->copy && result->mode == MODE_CLIENT; result->poll_target = params->poll_target; if (params->nts) { @@ -1766,8 +1769,16 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr, inst->tx_count = 0; SRC_UpdateReachability(inst->source, synced_packet); - if (synced_packet) - SRC_UpdateStatus(inst->source, MAX(message->stratum, inst->min_stratum), pkt_leap); + + if (synced_packet) { + if (inst->copy && inst->remote_stratum > 0) { + /* Assume the reference ID and stratum of the server */ + inst->remote_stratum--; + SRC_SetRefid(inst->source, ntohl(message->reference_id), &inst->remote_addr.ip_addr); + } + + SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap); + } if (good_packet) { /* Adjust the polling interval, accumulate the sample, etc. */ diff --git a/srcparams.h b/srcparams.h index 81ad889..6211ec7 100644 --- a/srcparams.h +++ b/srcparams.h @@ -54,6 +54,7 @@ typedef struct { int sel_options; int nts; int nts_port; + int copy; uint32_t authkey; uint32_t cert_set; double max_delay; diff --git a/test/simulation/141-copy b/test/simulation/141-copy new file mode 100755 index 0000000..80e56bc --- /dev/null +++ b/test/simulation/141-copy @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +. ./test.common + +test_start "copy option" + +check_config_h 'FEAT_CMDMON 1' || test_skip + +client_server_options="copy" +chronyc_conf="tracking" + +run_test || test_fail +check_chronyd_exit || test_fail +check_source_selection || test_fail +check_sync || test_fail +check_chronyc_output "^Reference ID *: 7F7F0101 \(192\.168\.123\.1\) +Stratum *: 1" || test_fail + +test_pass