blob: e7fb60fdb71acfdfaf13105dc5b099a1155cda60 [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
*
* 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 "util.h"
#include "logging.h"
#include "local.h"
#include "memory.h"
#include "nameserv_async.h"
#include "privops.h"
#include "sched.h"
/* ================================================== */
/* 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 */
NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source, may be NULL */
int pool; /* Number 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 */
} 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;
/* Source with unknown address (which may be resolved later) */
struct UnresolvedSource {
char *name;
int port;
int random_order;
int replacement;
union {
struct {
NTP_Source_Type type;
SourceParameters params;
int pool;
int max_new_sources;
} new_source;
NTP_Remote_Address replace_source;
};
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 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 sources added from this pool (ignoring tentative sources) */
int sources;
/* Maximum number of sources */
int max_sources;
};
/* Array of SourcePool */
static ARR_Instance pools;
/* ================================================== */
/* Forward prototypes */
static void resolve_sources(void *arg);
static void rehash_records(void);
static void clean_source_record(SourceRecord *record);
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);
}
/* ================================================== */
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)
{
SourceRecord *record;
struct UnresolvedSource *us;
unsigned int i;
ARR_DestroyInstance(pools);
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr)
clean_source_record(record);
}
ARR_DestroyInstance(records);
while (unresolved_sources) {
us = unresolved_sources;
unresolved_sources = us->next;
Free(us->name);
Free(us);
}
initialised = 0;
}
/* ================================================== */
/* Return slot number and whether the IP address was matched or not.
found = 0 => Neither IP nor port matched, empty slot returned
found = 1 => Only IP matched, port doesn't match
found = 2 => Both IP and port matched.
It is assumed that there can only ever be one record for a
particular IP address. (If a different port comes up, it probably
means someone is running ntpdate -d or something). Thus, if we
match the IP address we stop the search regardless of whether the
port number matches.
*/
static void
find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
{
SourceRecord *record;
uint32_t hash;
unsigned int i, size;
unsigned short port;
size = ARR_GetSize(records);
*slot = 0;
*found = 0;
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6)
return;
hash = UTI_IPToHash(&remote_addr->ip_addr);
port = remote_addr->port;
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,
&remote_addr->ip_addr, NULL)) {
*found = record->remote_addr->port == port ? 2 : 1;
return;
}
}
}
/* ================================================== */
/* 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, found;
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;
find_slot(temp_records[i].remote_addr, &slot, &found);
assert(!found);
*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)
{
SourceRecord *record;
int slot, found;
assert(initialised);
/* Find empty bin & check that we don't have the address already */
find_slot(remote_addr, &slot, &found);
if (found) {
return NSR_AlreadyInUse;
} else {
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6) {
return NSR_InvalidAF;
} else {
n_sources++;
if (!check_hashtable_size(n_sources, ARR_GetSize(records))) {
rehash_records();
find_slot(remote_addr, &slot, &found);
}
assert(!found);
record = get_record(slot);
record->data = NCR_GetInstance(remote_addr, type, params);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->name = name ? Strdup(name) : NULL;
record->pool = pool;
record->tentative = 1;
if (auto_start_sources)
NCR_StartInstance(record->data);
return NSR_Success;
}
}
}
/* ================================================== */
static NSR_Status
replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{
int slot1, slot2, found;
SourceRecord *record;
struct SourcePool *pool;
find_slot(old_addr, &slot1, &found);
if (!found)
return NSR_NoSuchSource;
find_slot(new_addr, &slot2, &found);
if (found)
return NSR_AlreadyInUse;
record = get_record(slot1);
NCR_ChangeRemoteAddress(record->data, new_addr);
record->remote_addr = NCR_GetRemoteAddress(record->data);
if (!record->tentative) {
record->tentative = 1;
if (record->pool != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources--;
}
}
/* The hash table must be rebuilt for the new address */
rehash_records();
LOG(LOGS_INFO, "Source %s replaced with %s",
UTI_IPToString(&old_addr->ip_addr),
UTI_IPToString(&new_addr->ip_addr));
return NSR_Success;
}
/* ================================================== */
static void
process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs)
{
NTP_Remote_Address address;
int i, added;
unsigned short first = 0;
if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first));
for (i = added = 0; i < n_addrs; i++) {
address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
address.port = us->port;
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
if (us->replacement) {
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
break;
} else {
if (add_source(&address, us->name, us->new_source.type, &us->new_source.params,
us->new_source.pool) == NSR_Success)
added++;
if (added >= us->new_source.max_new_sources)
break;
}
}
}
/* ================================================== */
static void
name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything)
{
struct UnresolvedSource *us, **i, *next;
us = (struct UnresolvedSource *)anything;
assert(us == resolving_source);
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;
/* Remove the source from the list on success or failure, replacements
are removed on any status */
if (us->replacement || status != DNS_TryAgain) {
for (i = &unresolved_sources; *i; i = &(*i)->next) {
if (*i == us) {
*i = us->next;
Free(us->name);
Free(us);
break;
}
}
}
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) {
if (resolving_interval < MIN_RESOLVE_INTERVAL)
resolving_interval = MIN_RESOLVE_INTERVAL;
else if (resolving_interval < MAX_RESOLVE_INTERVAL)
resolving_interval++;
resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT *
(1 << resolving_interval), resolve_sources, NULL);
} else {
resolving_interval = 0;
}
/* This round of resolving is done */
if (resolving_end_handler)
(resolving_end_handler)();
}
}
/* ================================================== */
static void
resolve_sources(void *arg)
{
struct UnresolvedSource *us;
assert(!resolving_source);
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;
}
/* ================================================== */
NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
{
return add_source(remote_addr, NULL, type, params, INVALID_POOL);
}
/* ================================================== */
void
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params)
{
struct UnresolvedSource *us;
struct SourcePool *sp;
NTP_Remote_Address remote_addr;
/* If the name is an IP address, don't bother with full resolving now
or later when trying to replace the source */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
NSR_AddSource(&remote_addr, type, params);
return;
}
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->port = port;
us->random_order = 0;
us->replacement = 0;
us->new_source.type = type;
us->new_source.params = *params;
if (!pool) {
us->new_source.pool = INVALID_POOL;
us->new_source.max_new_sources = 1;
} else {
sp = (struct SourcePool *)ARR_GetNewElement(pools);
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;
}
append_unresolved_source(us);
}
/* ================================================== */
void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{
resolving_end_handler = handler;
}
/* ================================================== */
void
NSR_ResolveSources(void)
{
/* Try to resolve unresolved sources now */
if (unresolved_sources) {
/* Make sure no resolving is currently running */
if (!resolving_source) {
if (resolving_interval) {
SCH_RemoveTimeout(resolving_id);
resolving_interval--;
}
resolve_sources(NULL);
}
} else {
/* No unresolved sources, we are done */
if (resolving_end_handler)
(resolving_end_handler)();
}
}
/* ================================================== */
void NSR_StartSources(void)
{
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
if (!get_record(i)->remote_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);
record->remote_addr = NULL;
NCR_DestroyInstance(record->data);
if (record->name)
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. Thus the caller can specify the port number
as zero if it wishes. */
NSR_Status
NSR_RemoveSource(NTP_Remote_Address *remote_addr)
{
int slot, found;
assert(initialised);
find_slot(remote_addr, &slot, &found);
if (!found) {
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_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", UTI_IPToString(&record->remote_addr->ip_addr));
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
us->port = record->remote_addr->port;
/* 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->replacement = 1;
us->replace_source = *record->remote_addr;
append_unresolved_source(us);
NSR_ResolveSources();
}
/* ================================================== */
void
NSR_HandleBadSource(IPAddr *address)
{
static struct timespec last_replacement;
struct timespec now;
NTP_Remote_Address remote_addr;
SourceRecord *record;
int slot, found;
double diff;
remote_addr.ip_addr = *address;
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return;
record = get_record(slot);
/* Only sources with a name can be replaced */
if (!record->name)
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 || !record->name)
continue;
resolve_source_replacement(record);
}
}
/* ================================================== */
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("removing tentative source %s",
UTI_IPToString(&record->remote_addr->ip_addr));
clean_source_record(record);
removed++;
}
if (removed)
rehash_records();
}
/* ================================================== */
uint32_t
NSR_GetLocalRefid(IPAddr *address)
{
NTP_Remote_Address remote_addr;
int slot, found;
remote_addr.ip_addr = *address;
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return 0;
return NCR_GetLocalRefid(get_record(slot)->data);
}
/* ================================================== */
/* 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, found;
assert(initialised);
find_slot(remote_addr, &slot, &found);
if (found == 2) { /* Must match IP address AND port number */
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 != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources++;
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources);
/* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */
if (pool->sources >= pool->max_sources)
remove_tentative_pool_sources(record->pool);
}
}
} 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, found;
find_slot(remote_addr, &slot, &found);
if (found == 2) { /* Must match IP address AND port number */
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_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
{
SourceRecord *record;
unsigned int i;
int any;
NSR_ResolveSources();
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_TakeSourceOnline(record->data);
}
}
}
if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us;
for (us = unresolved_sources; us; us = us->next) {
if (us->replacement)
continue;
any = 1;
us->new_source.params.online = 1;
}
}
return any;
}
/* ================================================== */
int
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
{
SourceRecord *record, *syncpeer;
unsigned int i, any;
any = 0;
syncpeer = NULL;
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;
if (NCR_IsSyncPeer(record->data)) {
syncpeer = record;
continue;
}
NCR_TakeSourceOffline(record->data);
}
}
}
/* Take sync peer offline as last to avoid reference switching */
if (syncpeer) {
NCR_TakeSourceOffline(syncpeer->data);
}
if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us;
for (us = unresolved_sources; us; us = us->next) {
if (us->replacement)
continue;
any = 1;
us->new_source.params.online = 0;
}
}
return any;
}
/* ================================================== */
int
NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
{
int slot, found;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found);
if (found == 0) {
return 0;
} else {
NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll);
return 1;
}
}
/* ================================================== */
int
NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
{
int slot, found;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found);
if (found == 0) {
return 0;
} else {
NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll);
return 1;
}
}
/* ================================================== */
int
NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
{
int slot, found;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found);
if (found == 0) {
return 0;
} else {
NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay);
return 1;
}
}
/* ================================================== */
int
NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
{
int slot, found;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found);
if (found == 0) {
return 0;
} else {
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, found;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found);
if (found == 0) {
return 0;
} else {
NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio);
return 1;
}
}
/* ================================================== */
int
NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
{
int slot, found;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found);
if (found == 0) {
return 0;
} else {
NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum);
return 1;
}
}
/* ================================================== */
int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{
int slot, found;
NTP_Remote_Address addr;
addr.ip_addr = *address;
addr.port = 0;
find_slot(&addr, &slot, &found);
if (found == 0) {
return 0;
} else {
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)
{
NTP_Remote_Address rem_addr;
int slot, found;
rem_addr.ip_addr = report->ip_addr;
rem_addr.port = 0;
find_slot(&rem_addr, &slot, &found);
if (found) {
NCR_ReportSource(get_record(slot)->data, report, now);
} else {
report->poll = 0;
report->latest_meas_ago = 0;
}
}
/* ================================================== */
/* The ip address is assumed to be completed on input, that is how we
identify the source record. */
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
NTP_Remote_Address rem_addr;
int slot, found;
rem_addr.ip_addr = report->remote_addr;
rem_addr.port = 0;
find_slot(&rem_addr, &slot, &found);
if (!found)
return 0;
NCR_GetNTPReport(get_record(slot)->data, report);
return 1;
}
/* ================================================== */
void
NSR_GetActivityReport(RPT_ActivityReport *report)
{
SourceRecord *record;
unsigned int i;
struct UnresolvedSource *us;
report->online = 0;
report->offline = 0;
report->burst_online = 0;
report->burst_offline = 0;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr) {
NCR_IncrementActivityCounters(record->data, &report->online, &report->offline,
&report->burst_online, &report->burst_offline);
}
}
report->unresolved = 0;
for (us = unresolved_sources; us; us = us->next) {
report->unresolved++;
}
}
/* ================================================== */