diff --git a/candm.h b/candm.h index 5b7c8d2..cf7c317 100644 --- a/candm.h +++ b/candm.h @@ -244,6 +244,7 @@ typedef struct { #define REQ_ADDSRC_PREFER 0x8 #define REQ_ADDSRC_NOSELECT 0x10 #define REQ_ADDSRC_TRUST 0x20 +#define REQ_ADDSRC_REQUIRE 0x40 typedef struct { IPAddr ip_addr; @@ -480,6 +481,7 @@ typedef struct { #define RPY_SD_FLAG_NOSELECT 0x1 #define RPY_SD_FLAG_PREFER 0x2 #define RPY_SD_FLAG_TRUST 0x4 +#define RPY_SD_FLAG_REQUIRE 0x8 typedef struct { IPAddr ip_addr; diff --git a/chrony.texi.in b/chrony.texi.in index 73f9023..dc8f817 100644 --- a/chrony.texi.in +++ b/chrony.texi.in @@ -2724,6 +2724,14 @@ which are not very accurate, but are locked with a PPS refclock. Assume time from this source is always true. It can't be rejected as a falseticker in the source selection if sources that are specified without this option don't agree with it. +@item require +Require that at least one of the sources specified with this option is +selectable (i.e. recently reachable and not a falseticker) before updating the +clock. Together with the @code{trust} option this may be useful to allow a +trusted, but not very precise, reference clock to be safely combined with +unauthenticated NTP sources in order to improve the accuracy of the clock. +They can be selected and used for synchronisation only if they agree with +the trusted and required source. @item minsamples Set the minimum number of samples kept for this source. This overrides the @code{minsamples} directive (@pxref{minsamples directive}). @@ -3033,6 +3041,14 @@ Assume time from this source is always true. It can't be rejected as a falseticker in the source selection if sources that are specified without this option don't agree with it. +@item require +Require that at least one of the sources specified with this option is +selectable (i.e. recently reachable and not a falseticker) before updating the +clock. Together with the @code{trust} option this may be useful to allow a +trusted authenticated source to be safely combined with unauthenticated sources +in order to improve the accuracy of the clock. They can be selected and used +for synchronisation only if they agree with the trusted and required source. + @item minsamples Set the minimum number of samples kept for this source. This overrides the @code{minsamples} directive (@pxref{minsamples directive}). diff --git a/client.c b/client.c index aa7271a..728f14e 100644 --- a/client.c +++ b/client.c @@ -1119,7 +1119,8 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line) (data.params.iburst ? REQ_ADDSRC_IBURST : 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)); + (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) | + (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0)); result = 1; break; diff --git a/cmdmon.c b/cmdmon.c index ae2a10c..3322cb7 100644 --- a/cmdmon.c +++ b/cmdmon.c @@ -664,7 +664,8 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message) tx_message->data.source_data.flags = htons((report.sel_options & SRC_SELECT_PREFER ? RPY_SD_FLAG_PREFER : 0) | (report.sel_options & SRC_SELECT_NOSELECT ? RPY_SD_FLAG_NOSELECT : 0) | - (report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0)); + (report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0) | + (report.sel_options & SRC_SELECT_REQUIRE ? RPY_SD_FLAG_REQUIRE : 0)); tx_message->data.source_data.reachability = htons(report.reachability); tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago); tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas); @@ -760,7 +761,8 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m 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) | - (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0); + (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) | + (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0); params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay); params.max_delay_ratio = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio); diff --git a/cmdparse.c b/cmdparse.c index 976e696..591e2e4 100644 --- a/cmdparse.c +++ b/cmdparse.c @@ -173,6 +173,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) } else if (!strcasecmp(cmd, "trust")) { src->params.sel_options |= SRC_SELECT_TRUST; + } else if (!strcasecmp(cmd, "require")) { + src->params.sel_options |= SRC_SELECT_REQUIRE; + } else if (!strcasecmp(cmd, "version")) { if (sscanf(line, "%d%n", &src->params.version, &n) != 1) { result = CPS_BadVersion; diff --git a/conf.c b/conf.c index 8260de6..df3d080 100644 --- a/conf.c +++ b/conf.c @@ -746,6 +746,9 @@ parse_refclock(char *line) } else if (!strcasecmp(cmd, "trust")) { n = 0; sel_options |= SRC_SELECT_TRUST; + } else if (!strcasecmp(cmd, "require")) { + n = 0; + sel_options |= SRC_SELECT_REQUIRE; } else { other_parse_error("Invalid refclock option"); return; diff --git a/sources.c b/sources.c index 2e48c5f..cee2cbf 100644 --- a/sources.c +++ b/sources.c @@ -602,7 +602,7 @@ SRC_SelectSource(SRC_Instance updated_inst) struct SelectInfo *si; struct timeval now, ref_time; int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources; - int n_badstats_sources, max_sel_reach, max_badstat_reach; + int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source; int depth, best_depth, trust_depth, best_trust_depth; int combined, stratum, min_stratum, max_score_index; double src_offset, src_offset_sd, src_frequency, src_skew; @@ -631,12 +631,18 @@ SRC_SelectSource(SRC_Instance updated_inst) n_endpoints = 0; n_sel_sources = 0; n_badstats_sources = 0; + sel_req_source = 0; max_sel_reach = max_badstat_reach = 0; max_reach_sample_ago = 0.0; for (i = 0; i < n_sources; i++) { assert(sources[i]->status != SRC_OK); + /* If some sources are specified with the require option, at least one + of them will have to be selectable in order to update the clock */ + if (sources[i]->sel_options & SRC_SELECT_REQUIRE) + sel_req_source = 1; + /* Ignore sources which were added with the noselect option */ if (sources[i]->sel_options & SRC_SELECT_NOSELECT) { sources[i]->status = SRC_UNSELECTABLE; @@ -825,15 +831,19 @@ SRC_SelectSource(SRC_Instance updated_inst) sources[i]->sel_info.hi_limit <= best_hi)) { sel_sources[n_sel_sources++] = i; + + if (sources[i]->sel_options & SRC_SELECT_REQUIRE) + sel_req_source = 0; } else { sources[i]->status = SRC_FALSETICKER; } } - if (n_sel_sources == 0 || n_sel_sources < CNF_GetMinSources()) { + if (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) { if (selected_source_index != INVALID_SOURCE) { log_selection_message("Can't synchronise: %s selectable sources", - n_sel_sources ? "not enough" : "no"); + !n_sel_sources ? "no" : + sel_req_source ? "no required source in" : "not enough"); selected_source_index = INVALID_SOURCE; } mark_ok_sources(SRC_WAITS_SOURCES); diff --git a/srcparams.h b/srcparams.h index 15fb6f2..42a1555 100644 --- a/srcparams.h +++ b/srcparams.h @@ -67,5 +67,6 @@ typedef struct { #define SRC_SELECT_NOSELECT 0x1 #define SRC_SELECT_PREFER 0x2 #define SRC_SELECT_TRUST 0x4 +#define SRC_SELECT_REQUIRE 0x8 #endif /* GOT_SRCPARAMS_H */