ntp: drop initstepslew NTP implementation

The initstepslew code has its own minimal NTP implementation. Drop the
code, add a new initstepslew mode to the reference updating code and
use regular NTP sources with iburst flag for initstepslew addresses
instead. When an update is made or a source is found unreachable, log a
message, remove the initstepslew sources and switch to normal mode.

This reduces code duplication and makes initstepslew use features
implemented only in the main code like source combining or SO_TIMESTAMP
support.
This commit is contained in:
Miroslav Lichvar 2014-04-07 17:27:44 +02:00
parent 4932f9d077
commit 7fda9c6723
12 changed files with 218 additions and 865 deletions

View file

@ -42,7 +42,7 @@ OBJS = util.o sched.o regress.o local.o \
sys.o main.o ntp_io.o ntp_core.o ntp_sources.o \
sources.o sourcestats.o reference.o \
logging.o conf.o cmdmon.o keys.o \
nameserv.o acquire.o manual.o addrfilt.o \
nameserv.o manual.o addrfilt.o \
cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
broadcast.o refclock.o refclock_phc.o refclock_pps.o \
refclock_shm.o refclock_sock.o tempcomp.o $(HASH_OBJ)

796
acquire.c
View file

@ -1,796 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
*
* 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.
*
**********************************************************************
=======================================================================
Processing to perform the equivalent of what ntpdate does. That is,
make a rapid-fire set of measurements to a designated set of
sources, and step or slew the local clock to bring it into line with
the result.
This is kept completely separate of the main chronyd processing, by
using a separate socket for sending/receiving the measurement
packets. That way, ntp_core.c can be kept completely independent of
this functionality.
A few of the finer points of how to construct valid RFC1305 packets
and validate responses for this case have been cribbed from the
ntpdate source.
*/
#include "config.h"
#include "sysincl.h"
#include "acquire.h"
#include "memory.h"
#include "sched.h"
#include "local.h"
#include "logging.h"
#include "ntp.h"
#include "util.h"
#include "main.h"
#include "conf.h"
/* ================================================== */
/* Interval between firing off the first sample to successive sources */
#define INTER_SOURCE_START (0.2)
#define MAX_SAMPLES 8
#define MAX_DEAD_PROBES 4
#define N_GOOD_SAMPLES 4
#define RETRANSMISSION_TIMEOUT (1.0)
#define NTP_VERSION 3
#define NTP_MAX_COMPAT_VERSION 4
#define NTP_MIN_COMPAT_VERSION 2
typedef struct {
IPAddr ip_addr; /* Address of the server */
int sanity; /* Flag indicating whether source
looks sane or not */
int n_dead_probes; /* Number of probes sent to the server
since a good one */
int n_samples; /* Number of samples accumulated */
int n_total_samples; /* Total number of samples received
including useless ones */
double offsets[MAX_SAMPLES]; /* In seconds, positive means local
clock is fast of reference */
double root_distances[MAX_SAMPLES]; /* in seconds */
double inter_lo; /* Low end of estimated range of offset */
double inter_hi; /* High end of estimated range of offset */
NTP_int64 last_tx; /* Transmit timestamp in last packet
transmitted to source. */
int timer_running;
SCH_TimeoutID timeout_id;
} SourceRecord;
static SourceRecord *sources;
static int n_sources;
static int n_started_sources;
static int n_completed_sources;
static double init_slew_threshold;
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef HAVE_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
static int sock_fd4 = -1;
#ifdef HAVE_IPV6
static int sock_fd6 = -1;
#endif
/* ================================================== */
static void (*saved_after_hook)(void *) = NULL;
static void *saved_after_hook_anything = NULL;
/* ================================================== */
typedef struct {
double offset;
enum {LO, HIGH} type;
int index;
} Endpoint;
typedef struct {
double lo;
double hi;
} Interval;
/* ================================================== */
static void read_from_socket(void *anything);
static void transmit_timeout(void *x);
static void wind_up_acquisition(void);
static void start_source_timeout_handler(void *not_used);
/* ================================================== */
static SCH_TimeoutID source_start_timeout_id;
/* ================================================== */
void
ACQ_Initialise(void)
{
}
/* ================================================== */
void
ACQ_Finalise(void)
{
}
/* ================================================== */
static int
prepare_socket(int family)
{
unsigned short port_number = CNF_GetAcquisitionPort();
int sock_fd;
socklen_t addrlen;
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
LOG_FATAL(LOGF_Acquire, "Could not open socket : %s", strerror(errno));
}
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
if (port_number == 0 || port_number == CNF_GetNTPPort()) {
/* Don't bother binding this socket - we're not fussed what port
number it gets */
} else {
union sockaddr_in46 my_addr;
memset(&my_addr, 0, sizeof (my_addr));
switch (family) {
case AF_INET:
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
addrlen = sizeof (my_addr.in4);
break;
#ifdef HAVE_IPV6
case AF_INET6:
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
my_addr.in6.sin6_addr = in6addr_any;
addrlen = sizeof (my_addr.in6);
break;
#endif
default:
assert(0);
}
if (bind(sock_fd, &my_addr.u, addrlen) < 0) {
LOG(LOGS_ERR, LOGF_Acquire, "Could not bind socket : %s", strerror(errno));
/* but keep running */
}
}
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
return sock_fd;
}
/* ================================================== */
static void
initialise_io(int family)
{
if (family == IPADDR_INET4 || family == IPADDR_UNSPEC)
sock_fd4 = prepare_socket(AF_INET);
#ifdef HAVE_IPV6
if (family == IPADDR_INET6 || family == IPADDR_UNSPEC)
sock_fd6 = prepare_socket(AF_INET6);
#endif
}
/* ================================================== */
static void
finalise_io(void)
{
if (sock_fd4 >= 0) {
SCH_RemoveInputFileHandler(sock_fd4);
close(sock_fd4);
}
sock_fd4 = -1;
#ifdef HAVE_IPV6
if (sock_fd6 >= 0) {
SCH_RemoveInputFileHandler(sock_fd6);
close(sock_fd6);
}
sock_fd6 = -1;
#endif
}
/* ================================================== */
static void
probe_source(SourceRecord *src)
{
NTP_Packet pkt;
int version = NTP_VERSION;
NTP_Mode my_mode = MODE_CLIENT;
struct timeval cooked;
union sockaddr_in46 his_addr;
int sock_fd;
socklen_t addrlen;
uint32_t ts_fuzz;
#if 0
printf("Sending probe to %s sent=%d samples=%d\n", UTI_IPToString(&src->ip_addr), src->n_probes_sent, src->n_samples);
#endif
pkt.lvm = (((LEAP_Unsynchronised << 6) & 0xc0) |
((version << 3) & 0x38) |
((my_mode) & 0x7));
pkt.stratum = 0;
pkt.poll = 4;
pkt.precision = -6; /* as ntpdate */
pkt.root_delay = UTI_DoubleToInt32(1.0); /* 1 second */
pkt.root_dispersion = UTI_DoubleToInt32(1.0); /* likewise */
pkt.reference_id = 0;
pkt.reference_ts.hi = 0; /* Set to 0 */
pkt.reference_ts.lo = 0; /* Set to 0 */
pkt.originate_ts.hi = 0; /* Set to 0 */
pkt.originate_ts.lo = 0; /* Set to 0 */
pkt.receive_ts.hi = 0; /* Set to 0 */
pkt.receive_ts.lo = 0; /* Set to 0 */
/* And do transmission */
memset(&his_addr, 0, sizeof (his_addr));
switch (src->ip_addr.family) {
case IPADDR_INET4:
his_addr.in4.sin_addr.s_addr = htonl(src->ip_addr.addr.in4);
his_addr.in4.sin_port = htons(123); /* Fixed for now */
his_addr.in4.sin_family = AF_INET;
addrlen = sizeof (his_addr.in4);
sock_fd = sock_fd4;
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
memcpy(&his_addr.in6.sin6_addr.s6_addr, &src->ip_addr.addr.in6,
sizeof (his_addr.in6.sin6_addr.s6_addr));
his_addr.in6.sin6_port = htons(123); /* Fixed for now */
his_addr.in6.sin6_family = AF_INET6;
addrlen = sizeof (his_addr.in6);
sock_fd = sock_fd6;
break;
#endif
default:
assert(0);
}
ts_fuzz = UTI_GetNTPTsFuzz(LCL_GetSysPrecisionAsLog());
LCL_ReadCookedTime(&cooked, NULL);
UTI_TimevalToInt64(&cooked, &pkt.transmit_ts, ts_fuzz);
if (sendto(sock_fd, (void *) &pkt, NTP_NORMAL_PACKET_SIZE,
0,
&his_addr.u, addrlen) < 0) {
LOG(LOGS_WARN, LOGF_Acquire, "Could not send to %s : %s",
UTI_IPToString(&src->ip_addr),
strerror(errno));
}
src->last_tx = pkt.transmit_ts;
++(src->n_dead_probes);
src->timer_running = 1;
src->timeout_id = SCH_AddTimeoutByDelay(RETRANSMISSION_TIMEOUT, transmit_timeout, (void *) src);
}
/* ================================================== */
static void
transmit_timeout(void *x)
{
SourceRecord *src = (SourceRecord *) x;
src->timer_running = 0;
#if 0
printf("Timeout expired for server %s\n", UTI_IPToString(&src->ip_addr));
#endif
if (src->n_dead_probes < MAX_DEAD_PROBES) {
probe_source(src);
} else {
/* Source has croaked or is taking too long to respond */
++n_completed_sources;
if (n_completed_sources == n_sources) {
wind_up_acquisition();
}
}
}
/* ================================================== */
#define MAX_STRATUM 15
static void
process_receive(NTP_Packet *msg, SourceRecord *src, struct timeval *now)
{
unsigned long lvm;
int leap, version, mode;
double root_delay, root_dispersion;
double total_root_delay, total_root_dispersion, total_root_distance;
struct timeval local_orig, local_average, remote_rx, remote_tx, remote_average;
double remote_interval, local_interval;
double delta, theta, epsilon;
int n;
/* Most of the checks are from ntpdate */
/* Need to do something about authentication */
lvm = msg->lvm;
leap = (lvm >> 6) & 0x3;
version = (lvm >> 3) & 0x7;
mode = lvm & 0x7;
if ((leap == LEAP_Unsynchronised) ||
(version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) ||
(mode != MODE_SERVER && mode != MODE_PASSIVE)) {
return;
}
if (msg->stratum > MAX_STRATUM) {
return;
}
/* Check whether server is responding to our last request */
if ((msg->originate_ts.hi != src->last_tx.hi) ||
(msg->originate_ts.lo != src->last_tx.lo)) {
return;
}
/* Check that the server is sane */
if (((msg->originate_ts.hi == 0) && (msg->originate_ts.lo == 0)) ||
((msg->receive_ts.hi == 0) && (msg->receive_ts.lo) == 0)) {
return;
}
root_delay = UTI_Int32ToDouble(msg->root_delay);
root_dispersion = UTI_Int32ToDouble(msg->root_dispersion);
UTI_Int64ToTimeval(&src->last_tx, &local_orig);
UTI_Int64ToTimeval(&msg->receive_ts, &remote_rx);
UTI_Int64ToTimeval(&msg->transmit_ts, &remote_tx);
UTI_AverageDiffTimevals(&remote_rx, &remote_tx, &remote_average, &remote_interval);
UTI_AverageDiffTimevals(&local_orig, now, &local_average, &local_interval);
delta = local_interval - remote_interval;
/* Defined as positive if we are fast. Note this sign convention is
opposite to that used in ntp_core.c */
UTI_DiffTimevalsToDouble(&theta, &local_average, &remote_average);
/* Could work out epsilon - leave till later */
epsilon = 0.0;
total_root_delay = fabs(delta) + root_delay;
total_root_dispersion = epsilon + root_dispersion;
total_root_distance = 0.5 * fabs(total_root_delay) + total_root_dispersion;
n = src->n_samples;
#if 0
printf("Sample %d theta=%.6f delta=%.6f root_del=%.6f root_disp=%.6f root_dist=%.6f\n",
n, theta, delta, total_root_delay, total_root_dispersion, total_root_distance);
#endif
src->offsets[n] = theta;
src->root_distances[n] = total_root_distance;
++(src->n_samples);
}
/* ================================================== */
static void
read_from_socket(void *anything)
{
int status;
ReceiveBuffer msg;
union sockaddr_in46 his_addr;
int sock_fd;
socklen_t his_addr_len;
int flags;
int message_length;
IPAddr remote_ip;
int i, ok;
struct timeval now;
SourceRecord *src;
flags = 0;
message_length = sizeof(msg);
his_addr_len = sizeof(his_addr);
/* Get timestamp */
SCH_GetLastEventTime(&now, NULL, NULL);
sock_fd = (long)anything;
status = recvfrom (sock_fd, (char *)&msg, message_length, flags,
&his_addr.u, &his_addr_len);
if (status < 0) {
LOG(LOGS_WARN, LOGF_Acquire, "Error reading from socket, %s", strerror(errno));
return;
}
switch (his_addr.u.sa_family) {
case AF_INET:
remote_ip.family = IPADDR_INET4;
remote_ip.addr.in4 = ntohl(his_addr.in4.sin_addr.s_addr);
break;
#ifdef HAVE_IPV6
case AF_INET6:
remote_ip.family = IPADDR_INET6;
memcpy(&remote_ip.addr.in6, his_addr.in6.sin6_addr.s6_addr,
sizeof (remote_ip.addr.in6));
break;
#endif
default:
assert(0);
}
#if 0
printf("Got message from %s\n", UTI_IPToString(&remote_ip));
#endif
/* Find matching host */
ok = 0;
for (i=0; i<n_sources; i++) {
if (UTI_CompareIPs(&remote_ip, &sources[i].ip_addr, NULL) == 0) {
ok = 1;
break;
}
}
if (ok) {
src = sources + i;
++src->n_total_samples;
src->n_dead_probes = 0; /* reset this when we actually receive something */
/* If we got into this function, we know the retransmission timeout has not
expired for the source */
if (src->timer_running) {
SCH_RemoveTimeout(src->timeout_id);
src->timer_running = 0;
}
process_receive(&msg.ntp_pkt, src, &now);
/* Check if server done and requeue timeout */
if ((src->n_samples >= N_GOOD_SAMPLES) ||
(src->n_total_samples >= MAX_SAMPLES)) {
++n_completed_sources;
#if 0
printf("Source %s completed\n", UTI_IPToString(&src->ip_addr));
#endif
if (n_completed_sources == n_sources) {
wind_up_acquisition();
}
} else {
/* Send the next probe */
probe_source(src);
}
}
}
/* ================================================== */
static void
start_next_source(void)
{
probe_source(sources + n_started_sources);
#if 0
printf("Trying to start source %s\n", UTI_IPToString(&sources[n_started_sources].ip_addr));
#endif
n_started_sources++;
if (n_started_sources < n_sources) {
source_start_timeout_id = SCH_AddTimeoutByDelay(INTER_SOURCE_START, start_source_timeout_handler, NULL);
}
}
/* ================================================== */
static int
endpoint_compare(const void *a, const void *b)
{
const Endpoint *aa = (const Endpoint *) a;
const Endpoint *bb = (const Endpoint *) b;
if (aa->offset < bb->offset) {
return -1;
} else if (aa->offset > bb->offset) {
return +1;
} else {
return 0;
}
}
/* ================================================== */
static void
process_measurements(void)
{
SourceRecord *s;
Endpoint *eps;
int i, j;
int n_sane_sources;
double lo, hi;
double inter_lo, inter_hi;
int depth;
int best_depth;
int n_at_best_depth;
Interval *intervals;
double estimated_offset;
int index1, index2;
n_sane_sources = 0;
/* First, get a consistent interval for each source. Those for
which this is not possible are considered to be insane. */
for (i=0; i<n_sources; i++) {
s = sources + i;
/* If we got no measurements, the source is insane */
if (s->n_samples == 0) {
s->sanity = 0;
} else {
s->sanity = 1; /* so far ... */
lo = s->offsets[0] - s->root_distances[0];
hi = s->offsets[0] + s->root_distances[0];
inter_lo = lo;
inter_hi = hi;
for (j=1; j<s->n_samples; j++) {
lo = s->offsets[j] - s->root_distances[j];
hi = s->offsets[j] + s->root_distances[j];
if ((inter_hi <= lo) || (inter_lo >= hi)) {
/* Oh dear, we won't get an interval for this source */
s->sanity = 0;
break;
} else {
inter_lo = (lo < inter_lo) ? inter_lo : lo;
inter_hi = (hi > inter_hi) ? inter_hi : hi;
}
}
if (s->sanity) {
s->inter_lo = inter_lo;
s->inter_hi = inter_hi;
}
}
if (s->sanity) {
++n_sane_sources;
}
}
/* Now build the endpoint list, similar to the RFC1305 clock
selection algorithm. */
eps = MallocArray(Endpoint, 2*n_sane_sources);
intervals = MallocArray(Interval, n_sane_sources);
j = 0;
for (i=0; i<n_sources; i++) {
s = sources + i;
if (s->sanity) {
eps[j].offset = s->inter_lo;
eps[j].type = LO;
eps[j].index = i;
eps[j+1].offset = s->inter_hi;
eps[j+1].type = HIGH;
eps[j+1].index = i;
j += 2;
}
}
qsort(eps, 2*n_sane_sources, sizeof(Endpoint), endpoint_compare);
/* Now do depth searching algorithm */
n_at_best_depth = best_depth = depth = 0;
for (i=0; i<2*n_sane_sources; i++) {
#if 0
fprintf(stderr, "Endpoint type %s source index %d [ip=%s] offset=%.6f\n",
(eps[i].type == LO) ? "LO" : "HIGH",
eps[i].index,
UTI_IPToString(&sources[eps[i].index].ip_addr),
eps[i].offset);
#endif
switch (eps[i].type) {
case LO:
depth++;
if (depth > best_depth) {
best_depth = depth;
n_at_best_depth = 0;
intervals[0].lo = eps[i].offset;
} else if (depth == best_depth) {
intervals[n_at_best_depth].lo = eps[i].offset;
} else {
/* Nothing to do */
}
break;
case HIGH:
if (depth == best_depth) {
intervals[n_at_best_depth].hi = eps[i].offset;
n_at_best_depth++;
}
depth--;
break;
}
}
if (best_depth > 0) {
if ((n_at_best_depth % 2) == 1) {
index1 = (n_at_best_depth - 1) / 2;
estimated_offset = 0.5 * (intervals[index1].lo + intervals[index1].hi);
} else {
index2 = (n_at_best_depth / 2);
index1 = index2 - 1;
estimated_offset = 0.5 * (intervals[index1].lo + intervals[index2].hi);
}
/* Apply a step change to the system clock. As per sign
convention in local.c and its children, a positive offset means
the system clock is fast of the reference, i.e. it needs to be
stepped backwards. */
if (fabs(estimated_offset) > init_slew_threshold) {
LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (step)",
fabs(estimated_offset),
(estimated_offset >= 0) ? "fast" : "slow");
LCL_ApplyStepOffset(estimated_offset);
} else {
LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (slew)",
fabs(estimated_offset),
(estimated_offset >= 0) ? "fast" : "slow");
LCL_AccumulateOffset(estimated_offset, 0.0);
}
} else {
LOG(LOGS_WARN, LOGF_Acquire, "No intersecting endpoints found");
}
Free(intervals);
Free(eps);
}
/* ================================================== */
static void
wind_up_acquisition(void)
{
/* Now process measurements */
process_measurements();
Free(sources);
finalise_io();
if (saved_after_hook) {
(saved_after_hook)(saved_after_hook_anything);
}
}
/* ================================================== */
static void
start_source_timeout_handler(void *not_used)
{
start_next_source();
}
/* ================================================== */
void
ACQ_StartAcquisition(int n, IPAddr *ip_addrs, double threshold, void (*after_hook)(void *), void *anything)
{
int i, ip4, ip6;
int k, duplicate_ip;
saved_after_hook = after_hook;
saved_after_hook_anything = anything;
init_slew_threshold = threshold;
n_started_sources = 0;
n_completed_sources = 0;
n_sources = 0;
sources = MallocArray(SourceRecord, n);
for (i = ip4 = ip6 = 0; i < n; i++) {
/* check for duplicate IP addresses and ignore them */
duplicate_ip = 0;
for (k = 0; k < i; k++) {
duplicate_ip |= UTI_CompareIPs(&(sources[k].ip_addr),
&ip_addrs[i],
NULL) == 0;
}
if (!duplicate_ip) {
sources[n_sources].ip_addr = ip_addrs[i];
sources[n_sources].n_samples = 0;
sources[n_sources].n_total_samples = 0;
sources[n_sources].n_dead_probes = 0;
if (ip_addrs[i].family == IPADDR_INET4)
ip4++;
else if (ip_addrs[i].family == IPADDR_INET6)
ip6++;
n_sources++;
} else {
LOG(LOGS_WARN, LOGF_Acquire, "Ignoring duplicate source: %s",
UTI_IPToString(&ip_addrs[i]));
}
}
initialise_io((ip4 && ip6) ? IPADDR_UNSPEC : (ip4 ? IPADDR_INET4 : IPADDR_INET6));
/* Start sampling first source */
start_next_source();
}
/* ================================================== */

View file

@ -1,45 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
*
* 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.
*
**********************************************************************
=======================================================================
Header file for acquisition module
*/
#ifndef GOT_ACQUIRE_H
#define GOT_ACQUIRE_H
#include "addressing.h"
typedef struct ACQ_SourceRecord *ACQ_Source;
extern void ACQ_Initialise(void);
extern void ACQ_Finalise(void);
extern void ACQ_StartAcquisition(int n, IPAddr *ip_addrs, double init_slew_threshold,
void (*after_hook)(void *), void *anything);
extern void ACQ_AccumulateSample(ACQ_Source acq_source, double offset, double root_distance);
extern void ACQ_MissedSample(ACQ_Source acq_source);
#endif /* GOT_ACQUIRE_H */

43
conf.c
View file

@ -38,7 +38,6 @@
#include "logging.h"
#include "nameserv.h"
#include "memory.h"
#include "acquire.h"
#include "cmdparse.h"
#include "broadcast.h"
#include "util.h"
@ -110,7 +109,6 @@ static char *dumpdir = ".";
static int enable_local=0;
static int local_stratum;
static int do_init_stepslew = 0;
static int n_init_srcs;
/* Threshold (in seconds) - if absolute value of initial error is less
@ -787,9 +785,6 @@ parse_initstepslew(char *line)
}
}
}
if (n_init_srcs > 0) {
do_init_stepslew = 1;
}
}
/* ================================================== */
@ -1155,12 +1150,24 @@ parse_include(char *line)
/* ================================================== */
void
CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything)
CNF_AddInitSources(void)
{
if (do_init_stepslew) {
ACQ_StartAcquisition(n_init_srcs, init_srcs_ip, init_slew_threshold, after_hook, anything);
} else {
(after_hook)(anything);
CPS_NTP_Source cps_source;
NTP_Remote_Address ntp_addr;
char dummy_hostname[2] = "H";
int i;
for (i = 0; i < n_init_srcs; i++) {
/* Get the default NTP params */
CPS_ParseNTPSourceAdd(dummy_hostname, &cps_source);
/* Add the address as an offline iburst server */
ntp_addr.ip_addr = init_srcs_ip[i];
ntp_addr.port = cps_source.port;
cps_source.params.iburst = 1;
cps_source.params.online = 0;
NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params);
}
}
@ -1671,3 +1678,19 @@ CNF_GetHwclockFile(void)
{
return hwclock_file;
}
/* ================================================== */
int
CNF_GetInitSources(void)
{
return n_init_srcs;
}
/* ================================================== */
double
CNF_GetInitStepThreshold(void)
{
return init_slew_threshold;
}

6
conf.h
View file

@ -35,12 +35,11 @@ extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename);
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void);
extern void CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything);
extern unsigned short CNF_GetAcquisitionPort(void);
extern unsigned short CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
@ -103,4 +102,7 @@ extern int CNF_GetMinSamples(void);
extern double CNF_GetRtcAutotrim(void);
extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
#endif /* GOT_CONF_H */

47
main.c
View file

@ -44,7 +44,6 @@
#include "conf.h"
#include "cmdmon.h"
#include "keys.h"
#include "acquire.h"
#include "manual.h"
#include "rtc.h"
#include "refclock.h"
@ -64,6 +63,8 @@ static int initialised = 0;
static int reload = 0;
static REF_Mode ref_mode;
/* ================================================== */
static void
@ -87,7 +88,6 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
ACQ_Finalise();
CLG_Finalise();
NSR_Finalise();
NCR_Finalise();
@ -123,8 +123,15 @@ signal_cleanup(int x)
/* ================================================== */
static void
post_acquire_hook(void *anything)
post_init_ntp_hook(void *anything)
{
if (ref_mode == REF_ModeInitStepSlew) {
/* Remove the initstepslew sources and set normal mode */
NSR_RemoveAllSources();
ref_mode = REF_ModeNormal;
REF_SetMode(ref_mode);
}
/* Close the pipe to the foreground process so it can exit */
LOG_CloseParentFd();
@ -145,10 +152,34 @@ post_acquire_hook(void *anything)
/* ================================================== */
static void
reference_mode_end(int result)
{
switch (ref_mode) {
case REF_ModeNormal:
break;
case REF_ModeInitStepSlew:
/* post_init_ntp_hook removes sources and a source call is
on the stack here, so it can't be called directly */
SCH_AddTimeoutByDelay(0.0, post_init_ntp_hook, NULL);
break;
default:
assert(0);
}
}
/* ================================================== */
static void
post_init_rtc_hook(void *anything)
{
CNF_ProcessInitStepSlew(post_acquire_hook, NULL);
if (CNF_GetInitSources() > 0) {
CNF_AddInitSources();
assert(REF_GetMode() != REF_ModeNormal);
/* Wait for mode end notification */
} else {
(post_init_ntp_hook)(NULL);
}
}
/* ================================================== */
@ -416,13 +447,19 @@ int main
NCR_Initialise();
NSR_Initialise();
CLG_Initialise();
ACQ_Initialise();
MNL_Initialise();
TMC_Initialise();
/* From now on, it is safe to do finalisation on exit */
initialised = 1;
if (CNF_GetInitSources() > 0) {
ref_mode = REF_ModeInitStepSlew;
}
REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode);
if (do_init_rtc) {
RTC_TimeInit(post_init_rtc_hook, NULL);
} else {

View file

@ -339,6 +339,21 @@ NSR_RemoveSource(NTP_Remote_Address *remote_addr)
/* ================================================== */
void
NSR_RemoveAllSources(void)
{
int i;
for (i = 0; i < N_RECORDS; i++) {
if (!records[i].remote_addr)
continue;
NCR_DestroyInstance(records[i].data);
records[i].remote_addr = NULL;
}
}
/* ================================================== */
/* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */
void

View file

@ -60,6 +60,9 @@ extern void NSR_ResolveSources(void);
/* Procedure to remove a source */
extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
/* Procedure to remove all sources */
extern void NSR_RemoveAllSources(void);
/* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);

View file

@ -67,6 +67,9 @@ static double correction_time_ratio;
/* Flag indicating that we are initialised */
static int initialised = 0;
/* Current operating mode */
static REF_Mode mode;
/* Threshold and update limit for stepping clock */
static int make_step_limit;
static double make_step_threshold;
@ -86,6 +89,9 @@ static int do_mail_change;
static double mail_change_threshold;
static char *mail_change_user;
/* Handler for mode ending */
static REF_ModeEndHandler mode_end_handler = NULL;
/* Filename of the drift file. */
static char *drift_file=NULL;
static double drift_file_age;
@ -155,6 +161,7 @@ REF_Initialise(void)
double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm;
mode = REF_ModeIgnore;
are_we_synchronised = 0;
our_leap_status = LEAP_Unsynchronised;
our_leap_sec = 0;
@ -266,6 +273,29 @@ REF_Finalise(void)
/* ================================================== */
void REF_SetMode(REF_Mode new_mode)
{
mode = new_mode;
}
/* ================================================== */
REF_Mode
REF_GetMode(void)
{
return mode;
}
/* ================================================== */
void
REF_SetModeEndHandler(REF_ModeEndHandler handler)
{
mode_end_handler = handler;
}
/* ================================================== */
static double
Sqr(double x)
{
@ -456,6 +486,18 @@ schedule_fb_drift(struct timeval *now)
/* ================================================== */
static void
end_ref_mode(int result)
{
mode = REF_ModeIgnore;
/* Dispatch the handler */
if (mode_end_handler)
(mode_end_handler)(result);
}
/* ================================================== */
#define BUFLEN 255
#define S_MAX_USER_LEN "128"
@ -664,6 +706,43 @@ write_log(struct timeval *ref_time, char *ref, int stratum, NTP_Leap leap,
/* ================================================== */
static void
special_mode_sync(int valid, double offset)
{
int step;
switch (mode) {
case REF_ModeInitStepSlew:
if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew");
end_ref_mode(0);
break;
}
step = fabs(offset) >= CNF_GetInitStepThreshold();
LOG(LOGS_INFO, LOGF_Reference,
"System's initial offset : %.6f seconds %s of true (%s)",
fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew");
if (step)
LCL_ApplyStepOffset(offset);
else
LCL_AccumulateOffset(offset, 0.0);
end_ref_mode(1);
break;
case REF_ModeIgnore:
/* Do nothing until the mode is changed */
break;
default:
assert(0);
}
}
/* ================================================== */
void
REF_SetReference(int stratum,
NTP_Leap leap,
@ -695,6 +774,12 @@ REF_SetReference(int stratum,
assert(initialised);
/* Special modes are implemented elsewhere */
if (mode != REF_ModeNormal) {
special_mode_sync(1, offset);
return;
}
/* Guard against dividing by zero */
if (skew < MIN_SKEW)
skew = MIN_SKEW;
@ -886,6 +971,12 @@ REF_SetUnsynchronised(void)
assert(initialised);
/* Special modes are implemented elsewhere */
if (mode != REF_ModeNormal) {
special_mode_sync(0, 0.0);
return;
}
/* This is cheaper than calling LCL_CookTime */
SCH_GetLastEventTime(&now, NULL, &now_raw);
UTI_DiffTimevalsToDouble(&uncorrected_offset, &now, &now_raw);
@ -1086,5 +1177,3 @@ REF_GetTrackingReport(RPT_TrackingReport *rep)
}
}
/* ================================================== */

View file

@ -40,6 +40,24 @@ extern void REF_Initialise(void);
/* Fini function */
extern void REF_Finalise(void);
typedef enum {
REF_ModeNormal,
REF_ModeInitStepSlew,
REF_ModeIgnore,
} REF_Mode;
/* Set reference update mode */
extern void REF_SetMode(REF_Mode mode);
/* Get reference update mode */
extern REF_Mode REF_GetMode(void);
/* Function type for handlers to be called back when mode ends */
typedef void (*REF_ModeEndHandler)(int result);
/* Set the handler for being notified of mode ending */
extern void REF_SetModeEndHandler(REF_ModeEndHandler handler);
/* Function which takes a local cooked time and returns the estimated
time of the reference. It also returns the other parameters
required for forming the outgoing NTP packet.

View file

@ -363,6 +363,12 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
/* Try to select a better source */
SRC_SelectSource(NULL);
}
/* End special reference mode on last reachability update from iburst */
if (REF_GetMode() != REF_ModeNormal &&
inst->reachability_size >= REACH_BITS - 1) {
REF_SetUnsynchronised();
}
}
/* ================================================== */

View file

@ -5,12 +5,13 @@ test_start "initstepslew directive"
freq_offset=0.0
wander=0.0
time_rms_limit=1e-3
limit=100
client_conf="initstepslew 5 192.168.123.1"
min_sync_time=15
max_sync_time=30
min_sync_time=20
max_sync_time=35
for time_offset in -2.0 -0.2 0.2 2.0; do
run_test || test_fail
@ -18,8 +19,8 @@ for time_offset in -2.0 -0.2 0.2 2.0; do
check_sync || test_fail
done
min_sync_time=1
max_sync_time=1
min_sync_time=5
max_sync_time=5
for time_offset in -1e8 -1e2 1e2 1e8; do
run_test || test_fail