diff --git a/chrony.texi.in b/chrony.texi.in index 441ad0d..6f195f3 100644 --- a/chrony.texi.in +++ b/chrony.texi.in @@ -2492,21 +2492,28 @@ pidfile /var/tmp/chronyd.pid @c {{{ pool @node pool directive @subsection pool -The syntax of this directive is identical to that for the @code{server} +The syntax of this directive is similar to that for the @code{server} directive (@pxref{server directive}), except that it is used to specify a pool of NTP servers rather than a single NTP server. The pool name is expected to -resolve to multiple addresses which change over time. +resolve to multiple addresses which may change over time. -On start, a source will be added for each resolved address. When a source from -the pool is unreachable or marked as falseticker, @code{chronyd} will try to -replace the source with a newly resolved address of the pool. +All options valid in the @code{server} directive can be used in this directive +too. There is one option specific to @code{pool} directive: @code{maxsources} +sets the maximum number of sources that can be used from the pool, the default +value is 4. + +On start, when the pool name is resolved, @code{chronyd} will add up to 16 +sources, one for each resolved address. When the number of sources from which +at least one valid reply was received reaches @code{maxsources}, the other +sources will be removed. When a pool source is unreachable or marked as +falseticker, @code{chronyd} will try to replace the source with a newly +resolved address of the pool. An example of the pool directive is @example -pool pool.ntp.org iburst +pool pool.ntp.org iburst maxsources 3 @end example - @c }}} @c {{{ port @node port directive diff --git a/client.c b/client.c index 2e1866c..0a390c0 100644 --- a/client.c +++ b/client.c @@ -960,6 +960,11 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line) break; } + if (data.params.max_sources != SRC_DEFAULT_MAXSOURCES) { + fprintf(stderr, "Option maxsources not supported\n"); + break; + } + msg->data.ntp_source.port = htonl((unsigned long) data.port); UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr); msg->data.ntp_source.minpoll = htonl(data.params.minpoll); @@ -1016,6 +1021,9 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line) case CPS_BadVersion: fprintf(stderr, "Unreadable version value\n"); break; + case CPS_BadMaxsources: + fprintf(stderr, "Unreadable maxsources value\n"); + break; } return result; diff --git a/cmdparse.c b/cmdparse.c index 5e589ef..88f98c2 100644 --- a/cmdparse.c +++ b/cmdparse.c @@ -60,6 +60,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) src->params.min_stratum = SRC_DEFAULT_MINSTRATUM; src->params.poll_target = SRC_DEFAULT_POLLTARGET; src->params.version = NTP_VERSION; + src->params.max_sources = SRC_DEFAULT_MAXSOURCES; src->params.sel_option = SRC_SelectNormal; result = CPS_Success; @@ -175,6 +176,14 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) line += n; } + } else if (!strcasecmp(cmd, "maxsources")) { + if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) { + result = CPS_BadMaxsources; + done = 1; + } else { + line += n; + } + } else { result = CPS_BadOption; done = 1; diff --git a/cmdparse.h b/cmdparse.h index df8484a..ae863b1 100644 --- a/cmdparse.h +++ b/cmdparse.h @@ -44,7 +44,8 @@ typedef enum { CPS_BadKey, CPS_BadMinstratum, CPS_BadPolltarget, - CPS_BadVersion + CPS_BadVersion, + CPS_BadMaxsources, } CPS_Status; typedef struct { diff --git a/conf.c b/conf.c index 8967dc8..3c26342 100644 --- a/conf.c +++ b/conf.c @@ -632,6 +632,9 @@ parse_source(char *line, NTP_Source_Type type, int pool) case CPS_BadVersion: other_parse_error("Unreadable version"); break; + case CPS_BadMaxsources: + other_parse_error("Unreadable maxsources"); + break; } } diff --git a/ntp_sources.c b/ntp_sources.c index 746bc0f..6fc05c4 100644 --- a/ntp_sources.c +++ b/ntp_sources.c @@ -51,6 +51,9 @@ typedef struct { NCR_Instance data; /* Data for the protocol engine for this source */ int pool; /* Number of the pool from which was this source added or INVALID_POOL */ + int tentative; /* Flag indicating there was no valid response + yet and the source may be removed if other + sources from the pool respond first */ } SourceRecord; /* Hash table of SourceRecord, the size should be a power of two */ @@ -98,6 +101,10 @@ static NSR_SourceResolvingEndHandler resolving_end_handler = NULL; struct SourcePool { char *name; int port; + /* Number of sources added from this pool (ignoring tentative sources) */ + int sources; + /* Maximum number of sources */ + int max_sources; }; /* Array of SourcePool */ @@ -317,6 +324,7 @@ add_source(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParamete record->data = NCR_GetInstance(remote_addr, type, params); record->remote_addr = NCR_GetRemoteAddress(record->data); record->pool = pool; + record->tentative = pool != INVALID_POOL ? 1 : 0; if (auto_start_sources) NCR_StartInstance(record->data); @@ -512,6 +520,8 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, Source sp = (struct SourcePool *)ARR_GetNewElement(pools); sp->name = Strdup(name); sp->port = port; + sp->sources = 0; + sp->max_sources = params->max_sources; us->new_source.pool = ARR_GetSize(pools) - 1; us->new_source.max_new_sources = MAX_POOL_SOURCES; } @@ -682,19 +692,62 @@ NSR_HandleBadSource(IPAddr *address) /* ================================================== */ +static void remove_tentative_pool_sources(int pool) +{ + SourceRecord *record; + unsigned int i, removed; + + for (i = removed = 0; i < ARR_GetSize(records); i++) { + record = get_record(i); + + if (!record->remote_addr || record->pool != pool || !record->tentative) + continue; + + DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s", + UTI_IPToString(&record->remote_addr->ip_addr)); + + clean_source_record(record); + removed++; + } + + if (removed) + rehash_records(); +} + /* This routine is called by ntp_io when a new packet arrives off the network, possibly with an authentication tail */ void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) { + SourceRecord *record; + struct SourcePool *pool; int slot, found; assert(initialised); find_slot(remote_addr, &slot, &found); if (found == 2) { /* Must match IP address AND port number */ - NCR_ProcessKnown(message, now, now_err, get_record(slot)->data, - local_addr, length); + record = get_record(slot); + + if (!NCR_ProcessKnown(message, now, now_err, record->data, local_addr, length)) + return; + + if (record->tentative) { + /* First reply from a pool source */ + record->tentative = 0; + + assert(record->pool != INVALID_POOL); + pool = (struct SourcePool *)ARR_GetElement(pools, record->pool); + pool->sources++; + + DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources", + pool->name, pool->sources); + + /* If the number of sources reached the configured maximum, remove + the tentative sources added from this pool */ + if (pool->sources >= pool->max_sources) + remove_tentative_pool_sources(record->pool); + } } else { NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length); } diff --git a/srcparams.h b/srcparams.h index 5fd7517..fdf0abb 100644 --- a/srcparams.h +++ b/srcparams.h @@ -39,6 +39,7 @@ typedef struct { int min_stratum; int poll_target; int version; + int max_sources; uint32_t authkey; double max_delay; double max_delay_ratio; @@ -55,6 +56,7 @@ typedef struct { #define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0 #define SRC_DEFAULT_MINSTRATUM 0 #define SRC_DEFAULT_POLLTARGET 6 +#define SRC_DEFAULT_MAXSOURCES 4 #define INACTIVE_AUTHKEY 0 #endif /* GOT_SRCPARAMS_H */