blob: 3cbb2ae7e6553f66605b3fd9a4d57ef355d46cc1 [file] [log] [blame]
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2021
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Functions which manage the pool of NTP sources that we are currently
a client of or peering with.
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "util.h"
#include "logging.h"
#include "local.h"
#include "memory.h"
#include "nameserv_async.h"
#include "privops.h"
#include "sched.h"
/* ================================================== */
/* Maximum number of sources */
#define MAX_SOURCES 65536
/* Record type private to this file, used to store information about
particular sources */
typedef struct {
NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL
means this slot in table is in use
(an IPADDR_ID address means the address
is not resolved yet) */
NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source as it was specified
(may be an IP address) */
int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response
received from the source yet */
uint32_t conf_id; /* Configuration ID, which can be shared with
different sources in case of a pool */
} SourceRecord;
/* Hash table of SourceRecord, its size is a power of two and it's never
more than half full */
static ARR_Instance records;
/* Number of sources in the hash table */
static int n_sources;
/* Flag indicating new sources will be started automatically when added */
static int auto_start_sources = 0;
/* Flag indicating a record is currently being modified */
static int record_lock;
/* Last assigned address ID */
static uint32_t last_address_id = 0;
/* Last assigned configuration ID */
static uint32_t last_conf_id = 0;
/* Source scheduled for name resolving (first resolving or replacement) */
struct UnresolvedSource {
/* Current address of the source (IPADDR_ID is used for a single source
with unknown address and IPADDR_UNSPEC for a pool of sources) */
NTP_Remote_Address address;
/* ID of the pool if not a single source */
int pool_id;
/* Name to be resolved */
char *name;
/* Flag indicating addresses should be used in a random order */
int random_order;
/* Next unresolved source in the list */
struct UnresolvedSource *next;
};
#define RESOLVE_INTERVAL_UNIT 7
#define MIN_RESOLVE_INTERVAL 2
#define MAX_RESOLVE_INTERVAL 9
#define MIN_REPLACEMENT_INTERVAL 8
static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0;
static int resolving_restart = 0;
static SCH_TimeoutID resolving_id;
static struct UnresolvedSource *resolving_source = NULL;
static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
#define MAX_POOL_SOURCES 16
#define INVALID_POOL (-1)
/* Pool of sources with the same name */
struct SourcePool {
/* Number of all sources from the pool */
int sources;
/* Number of sources with unresolved address */
int unresolved_sources;
/* Number of non-tentative sources */
int confirmed_sources;
/* Maximum number of confirmed sources */
int max_sources;
};
/* Array of SourcePool (indexed by their ID) */
static ARR_Instance pools;
/* Requested update of a source's address */
struct AddressUpdate {
NTP_Remote_Address old_address;
NTP_Remote_Address new_address;
};
/* Update saved when record_lock is true */
static struct AddressUpdate saved_address_update;
/* ================================================== */
/* Forward prototypes */
static void resolve_sources(void);
static void rehash_records(void);
static void handle_saved_address_update(void);
static void clean_source_record(SourceRecord *record);
static void remove_pool_sources(int pool_id, int tentative, int unresolved);
static void remove_unresolved_source(struct UnresolvedSource *us);
static void
slew_sources(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
void *anything);
/* ================================================== */
/* Flag indicating whether module is initialised */
static int initialised = 0;
/* ================================================== */
static SourceRecord *
get_record(unsigned index)
{
return (SourceRecord *)ARR_GetElement(records, index);
}
/* ================================================== */
static struct SourcePool *
get_pool(unsigned index)
{
return (struct SourcePool *)ARR_GetElement(pools, index);
}
/* ================================================== */
void
NSR_Initialise(void)
{
n_sources = 0;
initialised = 1;
records = ARR_CreateInstance(sizeof (SourceRecord));
rehash_records();
pools = ARR_CreateInstance(sizeof (struct SourcePool));
LCL_AddParameterChangeHandler(slew_sources, NULL);
}
/* ================================================== */
void
NSR_Finalise(void)
{
NSR_RemoveAllSources();
LCL_RemoveParameterChangeHandler(slew_sources, NULL);
ARR_DestroyInstance(records);
ARR_DestroyInstance(pools);
while (unresolved_sources)
remove_unresolved_source(unresolved_sources);
initialised = 0;
}
/* ================================================== */
/* Find a slot matching an IP address. It is assumed that there can
only ever be one record for a particular IP address. */
static int
find_slot(IPAddr *ip_addr, int *slot)
{
SourceRecord *record;
uint32_t hash;
unsigned int i, size;
size = ARR_GetSize(records);
*slot = 0;
switch (ip_addr->family) {
case IPADDR_INET4:
case IPADDR_INET6:
case IPADDR_ID:
break;
default:
return 0;
}
hash = UTI_IPToHash(ip_addr);
for (i = 0; i < size / 2; i++) {
/* Use quadratic probing */
*slot = (hash + (i + i * i) / 2) % size;
record = get_record(*slot);
if (!record->remote_addr)
break;
if (UTI_CompareIPs(&record->remote_addr->ip_addr, ip_addr, NULL) == 0)
return 1;
}
return 0;
}
/* ================================================== */
/* Find a slot matching an IP address and port. The function returns:
0 => IP not matched, empty slot returned if a valid address was provided
1 => Only IP matched, port doesn't match
2 => Both IP and port matched. */
static int
find_slot2(NTP_Remote_Address *remote_addr, int *slot)
{
if (!find_slot(&remote_addr->ip_addr, slot))
return 0;
return get_record(*slot)->remote_addr->port == remote_addr->port ? 2 : 1;
}
/* ================================================== */
/* Check if hash table of given size is sufficient to contain sources */
static int
check_hashtable_size(unsigned int sources, unsigned int size)
{
return sources * 2 <= size;
}
/* ================================================== */
static void
rehash_records(void)
{
SourceRecord *temp_records;
unsigned int i, old_size, new_size;
int slot;
assert(!record_lock);
old_size = ARR_GetSize(records);
temp_records = MallocArray(SourceRecord, old_size);
memcpy(temp_records, ARR_GetElements(records), old_size * sizeof (SourceRecord));
/* The size of the hash table is always a power of two */
for (new_size = 1; !check_hashtable_size(n_sources, new_size); new_size *= 2)
;
ARR_SetSize(records, new_size);
for (i = 0; i < new_size; i++)
get_record(i)->remote_addr = NULL;
for (i = 0; i < old_size; i++) {
if (!temp_records[i].remote_addr)
continue;
if (find_slot2(temp_records[i].remote_addr, &slot) != 0)
assert(0);
*get_record(slot) = temp_records[i];
}
Free(temp_records);
}
/* ================================================== */
/* Procedure to add a new source */
static NSR_Status
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
SourceParameters *params, int pool_id, uint32_t conf_id)
{
SourceRecord *record;
int slot;
assert(initialised);
/* Find empty bin & check that we don't have the address already */
if (find_slot2(remote_addr, &slot) != 0) {
return NSR_AlreadyInUse;
} else if (!name && !UTI_IsIPReal(&remote_addr->ip_addr)) {
/* Name is required for non-real addresses */
return NSR_InvalidName;
} else if (n_sources >= MAX_SOURCES) {
return NSR_TooManySources;
} else {
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6 &&
remote_addr->ip_addr.family != IPADDR_ID) {
return NSR_InvalidAF;
} else {
n_sources++;
if (!check_hashtable_size(n_sources, ARR_GetSize(records))) {
rehash_records();
if (find_slot2(remote_addr, &slot) != 0)
assert(0);
}
assert(!record_lock);
record_lock = 1;
record = get_record(slot);
assert(!name || !UTI_IsStringIP(name));
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->pool_id = pool_id;
record->tentative = 1;
record->conf_id = conf_id;
record_lock = 0;
if (record->pool_id != INVALID_POOL) {
get_pool(record->pool_id)->sources++;
if (!UTI_IsIPReal(&remote_addr->ip_addr))
get_pool(record->pool_id)->unresolved_sources++;
}
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
NCR_StartInstance(record->data);
/* The new instance is allowed to change its address immediately */
handle_saved_address_update();
return NSR_Success;
}
}
}
/* ================================================== */
static NSR_Status
change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr,
int replacement)
{
int slot1, slot2, found;
SourceRecord *record;
LOG_Severity severity;
char *name;
found = find_slot2(old_addr, &slot1);
if (found != 2)
return NSR_NoSuchSource;
/* Make sure there is no other source using the new address (with the same
or different port), but allow a source to have its port changed */
found = find_slot2(new_addr, &slot2);
if (found == 2 || (found != 0 && slot1 != slot2))
return NSR_AlreadyInUse;
assert(!record_lock);
record_lock = 1;
record = get_record(slot1);
NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
assert(0);
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
if (auto_start_sources)
NCR_StartInstance(record->data);
if (record->pool_id != INVALID_POOL)
get_pool(record->pool_id)->unresolved_sources--;
}
if (!record->tentative) {
record->tentative = 1;
if (record->pool_id != INVALID_POOL)
get_pool(record->pool_id)->confirmed_sources--;
}
record_lock = 0;
name = record->name;
severity = UTI_IsIPReal(&old_addr->ip_addr) ? LOGS_INFO : LOGS_DEBUG;
if (found == 0) {
/* The hash table must be rebuilt for the changed address */
rehash_records();
LOG(severity, "Source %s %s %s (%s)", UTI_IPToString(&old_addr->ip_addr),
replacement ? "replaced with" : "changed to",
UTI_IPToString(&new_addr->ip_addr), name);
} else {
LOG(severity, "Source %s (%s) changed port to %d",
UTI_IPToString(&new_addr->ip_addr), name, new_addr->port);
}
return NSR_Success;
}
/* ================================================== */
static void
handle_saved_address_update(void)
{
if (!UTI_IsIPReal(&saved_address_update.old_address.ip_addr))
return;
if (change_source_address(&saved_address_update.old_address,
&saved_address_update.new_address, 0) != NSR_Success)
/* This is expected to happen only if the old address is wrong */
LOG(LOGS_ERR, "Could not change %s to %s",
UTI_IPSockAddrToString(&saved_address_update.old_address),
UTI_IPSockAddrToString(&saved_address_update.new_address));
saved_address_update.old_address.ip_addr.family = IPADDR_UNSPEC;
}
/* ================================================== */
static int
replace_source_connectable(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{
if (!NIO_IsServerConnectable(new_addr)) {
DEBUG_LOG("%s not connectable", UTI_IPToString(&new_addr->ip_addr));
return 0;
}
if (change_source_address(old_addr, new_addr, 1) == NSR_AlreadyInUse)
return 0;
handle_saved_address_update();
return 1;
}
/* ================================================== */
static void
process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs)
{
NTP_Remote_Address old_addr, new_addr;
SourceRecord *record;
unsigned short first = 0;
int i, j;
if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first));
for (i = 0; i < n_addrs; i++) {
new_addr.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
if (us->pool_id != INVALID_POOL) {
/* In the pool resolving mode, try to replace a source from
the pool which does not have a real address yet */
for (j = 0; j < ARR_GetSize(records); j++) {
record = get_record(j);
if (!record->remote_addr || record->pool_id != us->pool_id ||
UTI_IsIPReal(&record->remote_addr->ip_addr))
continue;
old_addr = *record->remote_addr;
new_addr.port = old_addr.port;
if (replace_source_connectable(&old_addr, &new_addr))
;
break;
}
} else {
new_addr.port = us->address.port;
if (replace_source_connectable(&us->address, &new_addr))
break;
}
}
}
/* ================================================== */
static int
is_resolved(struct UnresolvedSource *us)
{
int slot;
if (us->pool_id != INVALID_POOL) {
return get_pool(us->pool_id)->unresolved_sources <= 0;
} else {
/* If the address is no longer present, it was removed or replaced
(i.e. resolved) */
return find_slot2(&us->address, &slot) == 0;
}
}
/* ================================================== */
static void
resolve_sources_timeout(void *arg)
{
resolving_id = 0;
resolve_sources();
}
/* ================================================== */
static void
name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything)
{
struct UnresolvedSource *us, *next;
us = (struct UnresolvedSource *)anything;
assert(us == resolving_source);
assert(resolving_id == 0);
DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs);
switch (status) {
case DNS_TryAgain:
break;
case DNS_Success:
process_resolved_name(us, ip_addrs, n_addrs);
break;
case DNS_Failure:
LOG(LOGS_WARN, "Invalid host %s", us->name);
break;
default:
assert(0);
}
next = us->next;
/* Don't repeat the resolving if it (permanently) failed, it was a
replacement of a real address, or all addresses are already resolved */
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us))
remove_unresolved_source(us);
/* If a restart was requested and this was the last source in the list,
start with the first source again (if there still is one) */
if (!next && resolving_restart) {
next = unresolved_sources;
resolving_restart = 0;
}
resolving_source = next;
if (next) {
/* Continue with the next source in the list */
DEBUG_LOG("resolving %s", next->name);
DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
} else {
/* This was the last source in the list. If some sources couldn't
be resolved, try again in exponentially increasing interval. */
if (unresolved_sources) {
resolving_interval = CLAMP(MIN_RESOLVE_INTERVAL, resolving_interval + 1,
MAX_RESOLVE_INTERVAL);
resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT * (1 << resolving_interval),
resolve_sources_timeout, NULL);
} else {
resolving_interval = 0;
}
/* This round of resolving is done */
if (resolving_end_handler)
(resolving_end_handler)();
}
}
/* ================================================== */
static void
resolve_sources(void)
{
struct UnresolvedSource *us, *next, *i;
assert(!resolving_source);
/* Remove sources that don't need to be resolved anymore */
for (i = unresolved_sources; i; i = next) {
next = i->next;
if (is_resolved(i))
remove_unresolved_source(i);
}
if (!unresolved_sources)
return;
PRV_ReloadDNS();
/* Start with the first source in the list, name_resolve_handler
will iterate over the rest */
us = unresolved_sources;
resolving_source = us;
DEBUG_LOG("resolving %s", us->name);
DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
}
/* ================================================== */
static void
append_unresolved_source(struct UnresolvedSource *us)
{
struct UnresolvedSource **i;
for (i = &unresolved_sources; *i; i = &(*i)->next)
;
*i = us;
us->next = NULL;
}
/* ================================================== */
static void
remove_unresolved_source(struct UnresolvedSource *us)
{
struct UnresolvedSource **i;
for (i = &unresolved_sources; *i; i = &(*i)->next) {
if (*i == us) {
*i = us->next;
Free(us->name);
Free(us);
break;
}
}
}
/* ================================================== */
static int get_unused_pool_id(void)
{
struct UnresolvedSource *us;
int i;
for (i = 0; i < ARR_GetSize(pools); i++) {
if (get_pool(i)->sources > 0)
continue;
/* Make sure there is no name waiting to be resolved using this pool */
for (us = unresolved_sources; us; us = us->next) {
if (us->pool_id == i)
break;
}
if (us)
continue;
return i;
}
return INVALID_POOL;
}
/* ================================================== */
NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
NSR_Status s;
s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
if (s != NSR_Success)
return s;
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
return s;
}
/* ================================================== */
NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
struct UnresolvedSource *us;
struct SourcePool *sp;
NTP_Remote_Address remote_addr;
int i, new_sources, pool_id;
/* If the name is an IP address, add the source with the address directly */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
return NSR_AddSource(&remote_addr, type, params, conf_id);
}
/* Make sure the name is at least printable and has no spaces */
for (i = 0; name[i] != '\0'; i++) {
if (!isgraph((unsigned char)name[i]))
return NSR_InvalidName;
}
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->random_order = 0;
remote_addr.ip_addr.family = IPADDR_ID;
remote_addr.ip_addr.addr.id = ++last_address_id;
remote_addr.port = port;
if (!pool) {
us->pool_id = INVALID_POOL;
us->address = remote_addr;
new_sources = 1;
} else {
pool_id = get_unused_pool_id();
if (pool_id != INVALID_POOL) {
sp = get_pool(pool_id);
} else {
sp = ARR_GetNewElement(pools);
pool_id = ARR_GetSize(pools) - 1;
}
sp->sources = 0;
sp->unresolved_sources = 0;
sp->confirmed_sources = 0;
sp->max_sources = CLAMP(1, params->max_sources, MAX_POOL_SOURCES);
us->pool_id = pool_id;
us->address.ip_addr.family = IPADDR_UNSPEC;
new_sources = MIN(2 * sp->max_sources, MAX_POOL_SOURCES);
}
append_unresolved_source(us);
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
for (i = 0; i < new_sources; i++) {
if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id;
if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success)
return NSR_TooManySources;
}
return NSR_UnresolvedName;
}
/* ================================================== */
void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{
resolving_end_handler = handler;
}
/* ================================================== */
void
NSR_ResolveSources(void)
{
/* Try to resolve unresolved sources now */
if (unresolved_sources) {
/* Allow only one resolving to be running at a time */
if (!resolving_source) {
if (resolving_id != 0) {
SCH_RemoveTimeout(resolving_id);
resolving_id = 0;
resolving_interval--;
}
resolve_sources();
} else {
/* Try again as soon as the current resolving ends */
resolving_restart = 1;
}
} else {
/* No unresolved sources, we are done */
if (resolving_end_handler)
(resolving_end_handler)();
}
}
/* ================================================== */
void NSR_StartSources(void)
{
NTP_Remote_Address *addr;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
addr = get_record(i)->remote_addr;
if (!addr || !UTI_IsIPReal(&addr->ip_addr))
continue;
NCR_StartInstance(get_record(i)->data);
}
}
/* ================================================== */
void NSR_AutoStartSources(void)
{
auto_start_sources = 1;
}
/* ================================================== */
static void
clean_source_record(SourceRecord *record)
{
assert(record->remote_addr);
if (record->pool_id != INVALID_POOL) {
struct SourcePool *pool = get_pool(record->pool_id);
pool->sources--;
if (!UTI_IsIPReal(&record->remote_addr->ip_addr))
pool->unresolved_sources--;
if (!record->tentative)
pool->confirmed_sources--;
if (pool->max_sources > pool->sources)
pool->max_sources = pool->sources;
}
record->remote_addr = NULL;
NCR_DestroyInstance(record->data);
Free(record->name);
n_sources--;
}
/* ================================================== */
/* Procedure to remove a source. We don't bother whether the port
address is matched - we're only interested in removing a record for
the right IP address. */
NSR_Status
NSR_RemoveSource(IPAddr *address)
{
int slot;
assert(initialised);
if (find_slot(address, &slot) == 0)
return NSR_NoSuchSource;
clean_source_record(get_record(slot));
/* Rehash the table to make sure there are no broken probe sequences.
This is costly, but it's not expected to happen frequently. */
rehash_records();
return NSR_Success;
}
/* ================================================== */
void
NSR_RemoveSourcesById(uint32_t conf_id)
{
SourceRecord *record;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr || record->conf_id != conf_id)
continue;
clean_source_record(record);
}
rehash_records();
}
/* ================================================== */
void
NSR_RemoveAllSources(void)
{
SourceRecord *record;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr)
continue;
clean_source_record(record);
}
rehash_records();
}
/* ================================================== */
static void
resolve_source_replacement(SourceRecord *record)
{
struct UnresolvedSource *us;
DEBUG_LOG("trying to replace %s (%s)",
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
/* If there never was a valid reply from this source (e.g. it was a bad
replacement), ignore the order of addresses from the resolver to not get
stuck to a pair of addresses if the order doesn't change, or a group of
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
us->random_order = record->tentative;
us->pool_id = INVALID_POOL;
us->address = *record->remote_addr;
append_unresolved_source(us);
NSR_ResolveSources();
}
/* ================================================== */
void
NSR_HandleBadSource(IPAddr *address)
{
static struct timespec last_replacement;
struct timespec now;
SourceRecord *record;
IPAddr ip_addr;
double diff;
int slot;
if (!find_slot(address, &slot))
return;
record = get_record(slot);
/* Don't try to replace a source specified by an IP address unless the
address changed since the source was added (e.g. by NTS-KE) */
if (UTI_StringToIP(record->name, &ip_addr) &&
UTI_CompareIPs(&record->remote_addr->ip_addr, &ip_addr, NULL) == 0)
return;
/* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now);
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG("replacement postponed");
return;
}
last_replacement = now;
resolve_source_replacement(record);
}
/* ================================================== */
void
NSR_RefreshAddresses(void)
{
SourceRecord *record;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr)
continue;
resolve_source_replacement(record);
}
}
/* ================================================== */
NSR_Status
NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{
int slot;
if (!UTI_IsIPReal(&old_addr->ip_addr) || !UTI_IsIPReal(&new_addr->ip_addr))
return NSR_InvalidAF;
if (UTI_CompareIPs(&old_addr->ip_addr, &new_addr->ip_addr, NULL) != 0 &&
find_slot(&new_addr->ip_addr, &slot))
return NSR_AlreadyInUse;
/* If a record is being modified (e.g. by change_source_address(), or the
source is just being created), postpone the change to avoid corruption */
if (!record_lock)
return change_source_address(old_addr, new_addr, 0);
if (UTI_IsIPReal(&saved_address_update.old_address.ip_addr))
return NSR_TooManySources;
saved_address_update.old_address = *old_addr;
saved_address_update.new_address = *new_addr;
return NSR_Success;
}
/* ================================================== */
static void remove_pool_sources(int pool_id, int tentative, int unresolved)
{
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_id != pool_id)
continue;
if ((tentative && !record->tentative) ||
(unresolved && UTI_IsIPReal(&record->remote_addr->ip_addr)))
continue;
DEBUG_LOG("removing %ssource %s", tentative ? "tentative " : "",
UTI_IPToString(&record->remote_addr->ip_addr));
clean_source_record(record);
removed++;
}
if (removed)
rehash_records();
}
/* ================================================== */
uint32_t
NSR_GetLocalRefid(IPAddr *address)
{
int slot;
if (!find_slot(address, &slot))
return 0;
return NCR_GetLocalRefid(get_record(slot)->data);
}
/* ================================================== */
char *
NSR_GetName(IPAddr *address)
{
int slot;
if (!find_slot(address, &slot))
return NULL;
return get_record(slot)->name;
}
/* ================================================== */
/* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */
void
NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
SourceRecord *record;
struct SourcePool *pool;
int slot;
assert(initialised);
/* Avoid unnecessary lookup if the packet cannot be a response from our
source. Otherwise, it must match both IP address and port number. */
if (NTP_LVM_TO_MODE(message->lvm) != MODE_CLIENT &&
find_slot2(remote_addr, &slot) == 2) {
record = get_record(slot);
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
return;
if (record->tentative) {
/* This was the first good reply from the source */
record->tentative = 0;
if (record->pool_id != INVALID_POOL) {
pool = get_pool(record->pool_id);
pool->confirmed_sources++;
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->confirmed_sources);
/* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */
if (pool->confirmed_sources >= pool->max_sources)
remove_pool_sources(record->pool_id, 1, 0);
}
}
} else {
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
}
}
/* ================================================== */
void
NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
{
SourceRecord *record;
int slot;
/* Avoid unnecessary lookup if the packet cannot be a request to our
source. Otherwise, it must match both IP address and port number. */
if (NTP_LVM_TO_MODE(message->lvm) != MODE_SERVER &&
find_slot2(remote_addr, &slot) == 2) {
record = get_record(slot);
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
} else {
NCR_ProcessTxUnknown(remote_addr, local_addr, tx_ts, message, length);
}
}
/* ================================================== */
static void
slew_sources(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
void *anything)
{
SourceRecord *record;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr) {
if (change_type == LCL_ChangeUnknownStep) {
NCR_ResetInstance(record->data);
NCR_ResetPoll(record->data);
} else {
NCR_SlewTimes(record->data, cooked, dfreq, doffset);
}
}
}
}
/* ================================================== */
int
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
SourceRecord *record, *syncpeer;
unsigned int i, any;
if (connectivity != SRC_OFFLINE)
NSR_ResolveSources();
any = 0;
syncpeer = NULL;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr) {
/* Ignore SRC_MAYBE_ONLINE connectivity change for unspecified unresolved
sources as they would always end up in the offline state */
if ((address->family == IPADDR_UNSPEC &&
(connectivity != SRC_MAYBE_ONLINE || UTI_IsIPReal(&record->remote_addr->ip_addr))) ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
any = 1;
if (NCR_IsSyncPeer(record->data)) {
syncpeer = record;
continue;
}
NCR_SetConnectivity(record->data, connectivity);
}
}
}
/* Set the sync peer last to avoid unnecessary reference switching */
if (syncpeer)
NCR_SetConnectivity(syncpeer->data, connectivity);
return any;
}
/* ================================================== */
int
NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll);
return 1;
}
/* ================================================== */
int
NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll);
return 1;
}
/* ================================================== */
int
NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay);
return 1;
}
/* ================================================== */
int
NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio);
return 1;
}
/* ================================================== */
int
NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio);
return 1;
}
/* ================================================== */
int
NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum);
return 1;
}
/* ================================================== */
int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target);
return 1;
}
/* ================================================== */
int
NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
IPAddr *mask, IPAddr *address)
{
SourceRecord *record;
unsigned int i;
int any;
any = 0;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr) {
if (address->family == IPADDR_UNSPEC ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
any = 1;
NCR_InitiateSampleBurst(record->data, n_good_samples, n_total_samples);
}
}
}
return any;
}
/* ================================================== */
/* The ip address is assumed to be completed on input, that is how we
identify the source record. */
void
NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
int slot;
if (find_slot(&report->ip_addr, &slot)) {
NCR_ReportSource(get_record(slot)->data, report, now);
} else {
report->poll = 0;
report->latest_meas_ago = 0;
}
}
/* ================================================== */
int
NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_GetAuthReport(get_record(slot)->data, report);
return 1;
}
/* ================================================== */
/* The ip address is assumed to be completed on input, that is how we
identify the source record. */
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
int slot;
if (!find_slot(&report->remote_addr, &slot))
return 0;
NCR_GetNTPReport(get_record(slot)->data, report);
return 1;
}
/* ================================================== */
void
NSR_GetActivityReport(RPT_ActivityReport *report)
{
SourceRecord *record;
unsigned int i;
report->online = 0;
report->offline = 0;
report->burst_online = 0;
report->burst_offline = 0;
report->unresolved = 0;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr)
continue;
if (!UTI_IsIPReal(&record->remote_addr->ip_addr)) {
report->unresolved++;
} else {
NCR_IncrementActivityCounters(record->data, &report->online, &report->offline,
&report->burst_online, &report->burst_offline);
}
}
}
/* ================================================== */
void
NSR_DumpAuthData(void)
{
SourceRecord *record;
int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr)
continue;
NCR_DumpAuthData(record->data);
}
}