ntp: add copy option

When separate client and server instances of chronyd are running on one
computer (e.g. for security or performance reasons) and are synchronized
to each other, the server instance provides a reference ID based on the
local address used for synchronization of its NTP clock, which breaks
detection of synchronization loops for its own clients.

Add a "copy" option to specify that the server and client are closely
related, no loop can form between them, and the client should assume the
reference ID and stratum of the server to fix detection of loops between
the server and clients of the client.
This commit is contained in:
Miroslav Lichvar 2021-04-14 15:58:51 +02:00
parent 635a9d3f5a
commit 84d2811800
8 changed files with 49 additions and 3 deletions

View file

@ -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;

View file

@ -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) |

View file

@ -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) |

View file

@ -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")) {

View file

@ -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 <<server,*server*>>
@ -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

View file

@ -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. */

View file

@ -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;

19
test/simulation/141-copy Executable file
View file

@ -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