ntp: limit number of pool sources

A new option can be now used in the pool directive: 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, 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 maxsources, the
other sources will be removed.
This commit is contained in:
Miroslav Lichvar 2014-11-26 16:40:33 +01:00
parent f2c80cae44
commit 308bcae257
7 changed files with 93 additions and 10 deletions

View file

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

View file

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

View file

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

View file

@ -44,7 +44,8 @@ typedef enum {
CPS_BadKey,
CPS_BadMinstratum,
CPS_BadPolltarget,
CPS_BadVersion
CPS_BadVersion,
CPS_BadMaxsources,
} CPS_Status;
typedef struct {

3
conf.c
View file

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

View file

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

View file

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