summaryrefslogtreecommitdiffstats
path: root/ntp_sources.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:43:21 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:43:21 +0000
commit104f986b0650b8f93540785d2bcf486905e49b62 (patch)
tree2b2ae5113d9b57425d4bb3f726e325316b87e00a /ntp_sources.c
parentInitial commit. (diff)
downloadchrony-104f986b0650b8f93540785d2bcf486905e49b62.tar.xz
chrony-104f986b0650b8f93540785d2bcf486905e49b62.zip
Adding upstream version 3.4.upstream/3.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ntp_sources.c')
-rw-r--r--ntp_sources.c1137
1 files changed, 1137 insertions, 0 deletions
diff --git a/ntp_sources.c b/ntp_sources.c
new file mode 100644
index 0000000..fa21654
--- /dev/null
+++ b/ntp_sources.c
@@ -0,0 +1,1137 @@
+/*
+ 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_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) {
+ 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_SetConnectivity(record->data, connectivity);
+ }
+ }
+ }
+
+ /* Set the sync peer last to avoid unnecessary reference switching */
+ if (syncpeer)
+ NCR_SetConnectivity(syncpeer->data, connectivity);
+
+ 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.connectivity = connectivity;
+ }
+ }
+
+ 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++;
+ }
+}
+
+
+/* ================================================== */
+