logging.o conf.o cmdmon.o md5.o keys.o \
nameserv.o acquire.o manual.o addrfilt.o \
cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
- broadcast.o
+ broadcast.o refclock.o
EXTRA_OBJS=@EXTRA_OBJECTS@
/* ================================================== */
+static char *
+UTI_RefidToString(unsigned long ref_id)
+{
+ unsigned int a, b, c, d;
+ static char result[64];
+ a = (ref_id>>24) & 0xff;
+ b = (ref_id>>16) & 0xff;
+ c = (ref_id>> 8) & 0xff;
+ d = (ref_id>> 0) & 0xff;
+ snprintf(result, sizeof(result), "%c%c%c%c", a, b, c, d);
+ return result;
+}
+
static char *
UTI_IPToDottedQuad(unsigned long ip)
{
resid_skew = (double) (ntohl(reply.data.source_data.resid_skew)) * 1.0e-3;
hostname_buf[25] = 0;
- if (no_dns) {
+ if (mode == RPY_SD_MD_REF) {
+ snprintf(hostname_buf, sizeof(hostname_buf), "%s", UTI_RefidToString(ip_addr));
+ } else if (no_dns) {
snprintf(hostname_buf, sizeof(hostname_buf), "%s", UTI_IPToDottedQuad(ip_addr));
} else {
dns_lookup = DNS_IPAddress2Name(ip_addr);
#include "rtc.h"
#include "pktlength.h"
#include "clientlog.h"
+#include "refclock.h"
/* ================================================== */
/* Get data */
LCL_ReadCookedTime(&now_corr, &local_clock_err);
if (SRC_ReportSource(ntohl(rx_message->data.source_data.index), &report, &now_corr)) {
- NSR_ReportSource(&report, &now_corr);
+ switch (SRC_GetType(ntohl(rx_message->data.source_data.index))) {
+ case SRC_NTP:
+ NSR_ReportSource(&report, &now_corr);
+ break;
+ case SRC_REFCLOCK:
+ RCL_ReportSource(&report, &now_corr);
+ break;
+ }
tx_message->status = htons(STT_SUCCESS);
tx_message->reply = htons(RPY_SOURCE_DATA);
#include "conf.h"
#include "ntp_sources.h"
#include "ntp_core.h"
+#include "refclock.h"
#include "cmdmon.h"
#include "srcparams.h"
#include "logging.h"
static void parse_acquisitionport(const char *);
static void parse_port(const char *);
static void parse_server(const char *);
+static void parse_refclock(const char *);
static void parse_local(const char *);
static void parse_manual(const char *);
static void parse_initstepslew(const char *);
static const Command commands[] = {
{"server", 6, parse_server},
{"peer", 4, parse_peer},
+ {"refclock", 8, parse_refclock},
{"acquisitionport", 15, parse_acquisitionport},
{"port", 4, parse_port},
{"driftfile", 9, parse_driftfile},
static NTP_Source ntp_sources[MAX_NTP_SOURCES];
static int n_ntp_sources = 0;
+#define MAX_RCL_SOURCES 8
+
+static RefclockParameters refclock_sources[MAX_RCL_SOURCES];
+static int n_refclock_sources = 0;
+
/* ================================================== */
typedef struct _AllowDeny {
/* ================================================== */
+static void
+parse_refclock(const char *line)
+{
+ int i, n, param, poll;
+ unsigned long ref_id;
+ double offset;
+ char name[5], cmd[10 + 1];
+ unsigned char ref[5];
+
+ i = n_refclock_sources;
+ if (i >= MAX_RCL_SOURCES)
+ return;
+
+ poll = 4;
+ offset = 0.0;
+ ref_id = 0;
+
+ if (sscanf(line, "%4s %d%n", name, ¶m, &n) != 2) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read refclock driver name and parameter at line %d", line_number);
+ return;
+ }
+
+ line += n;
+
+ while (sscanf(line, "%10s%n", cmd, &n) == 1) {
+ line += n;
+ if (!strncasecmp(cmd, "refid", 5)) {
+ if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
+ break;
+ ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
+ } else if (!strncasecmp(cmd, "poll", 4)) {
+ if (sscanf(line, "%d%n", &poll, &n) != 1) {
+ break;
+ }
+ } else if (!strncasecmp(cmd, "offset", 6)) {
+ if (sscanf(line, "%lf%n", &offset, &n) != 1)
+ break;
+ } else {
+ LOG(LOGS_WARN, LOGF_Configure, "Unknown refclock parameter %s at line %d", cmd, line_number);
+ break;
+ }
+ line += n;
+ }
+
+ strncpy(refclock_sources[i].driver_name, name, 4);
+ refclock_sources[i].driver_parameter = param;
+ refclock_sources[i].poll = poll;
+ refclock_sources[i].offset = offset;
+ refclock_sources[i].ref_id = ref_id;
+
+ n_refclock_sources++;
+}
+
+/* ================================================== */
+
static void
parse_some_port(const char *line, int *portvar)
{
/* ================================================== */
+void
+CNF_AddRefclocks(void) {
+ int i;
+
+ for (i=0; i<n_refclock_sources; i++) {
+ RCL_AddRefclock(&refclock_sources[i]);
+ }
+}
+
+/* ================================================== */
+
void
CNF_AddBroadcasts(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);
LOGF_SysSolaris,
LOGF_SysSunOS,
LOGF_SysWinnt,
- LOGF_RtcLinux
+ LOGF_RtcLinux,
+ LOGF_Refclock
} LOG_Facility;
/* Init function */
#include "manual.h"
#include "version.h"
#include "rtc.h"
+#include "refclock.h"
#include "clientlog.h"
#include "broadcast.h"
SRC_Finalise();
SST_Finalise();
REF_Finalise();
+ RCL_Finalise();
RTC_Finalise();
CAM_Finalise();
NIO_Finalise();
CNF_SetupAccessRestrictions();
RTC_StartMeasurements();
+ RCL_StartRefclocks();
}
/* ================================================== */
NIO_Initialise();
CAM_Initialise();
RTC_Initialise();
+ RCL_Initialise();
if (SchedPriority > 0) {
SYS_SetScheduler(SchedPriority);
result->local_poll = params->minpoll;
/* Create a source instance for this NTP source */
- result->source = SRC_CreateNewInstance(remote_addr->ip_addr); /* Will need extra params eventually */
+ result->source = SRC_CreateNewInstance(remote_addr->ip_addr, SRC_NTP);
result->local_rx.tv_sec = 0;
result->local_rx.tv_usec = 0;
--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 2009
+ *
+ * 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.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Routines implementing reference clocks.
+
+ */
+
+#include "refclock.h"
+#include "conf.h"
+#include "local.h"
+#include "util.h"
+#include "sources.h"
+#include "logging.h"
+#include "sched.h"
+
+struct RCL_Instance_Record {
+ RefclockDriver *driver;
+ void *data;
+ int driver_parameter;
+ int poll;
+ int missed_samples;
+ unsigned long ref_id;
+ double offset;
+ SCH_TimeoutID timeout_id;
+ SRC_Instance source;
+};
+
+#define MAX_RCL_SOURCES 8
+
+static struct RCL_Instance_Record refclocks[MAX_RCL_SOURCES];
+static int n_sources = 0;
+
+static void poll_timeout(void *arg);
+
+void
+RCL_Initialise(void)
+{
+ CNF_AddRefclocks();
+}
+
+void
+RCL_Finalise(void)
+{
+ int i;
+
+ for (i = 0; i < n_sources; i++) {
+ RCL_Instance inst = (RCL_Instance)&refclocks[i];
+
+ if (inst->driver->fini)
+ inst->driver->fini(inst);
+ }
+}
+
+int
+RCL_AddRefclock(RefclockParameters *params)
+{
+ RCL_Instance inst = &refclocks[n_sources];
+
+ if (n_sources == MAX_RCL_SOURCES)
+ return 0;
+
+ if (0) {
+ } else {
+ LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
+ return 0;
+ }
+
+ inst->data = NULL;
+ inst->driver_parameter = params->driver_parameter;
+ inst->poll = params->poll;
+ inst->missed_samples = 0;
+ inst->offset = params->offset;
+ inst->timeout_id = -1;
+ inst->source = NULL;
+
+ if (params->ref_id)
+ inst->ref_id = params->ref_id;
+ else {
+ unsigned char ref[5] = { 0, 0, 0, 0, 0 };
+
+ snprintf((char *)ref, 5, "%s%d", params->driver_name, params->driver_parameter);
+ inst->ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
+ }
+
+ if (inst->driver->init)
+ if (!inst->driver->init(inst)) {
+ LOG_FATAL(LOGF_Refclock, "refclock %s initialisation failed", params->driver_name);
+ return 0;
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Refclock, "refclock added");
+#endif
+ n_sources++;
+
+ return 1;
+}
+
+void
+RCL_StartRefclocks(void)
+{
+ int i;
+
+ for (i = 0; i < n_sources; i++) {
+ RCL_Instance inst = &refclocks[i];
+
+ inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK);
+ if (inst->driver->poll)
+ inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
+ }
+}
+
+void
+RCL_ReportSource(RPT_SourceReport *report, struct timeval *now)
+{
+ int i;
+ unsigned long ref_id;
+
+ ref_id = report->ip_addr;
+
+ for (i = 0; i < n_sources; i++) {
+ RCL_Instance inst = &refclocks[i];
+ if (inst->ref_id == ref_id) {
+ report->poll = inst->poll;
+ report->mode = RPT_LOCAL_REFERENCE;
+ break;
+ }
+ }
+}
+
+void
+RCL_SetDriverData(RCL_Instance instance, void *data)
+{
+ instance->data = data;
+}
+
+void *
+RCL_GetDriverData(RCL_Instance instance)
+{
+ return instance->data;
+}
+
+int
+RCL_GetDriverParameter(RCL_Instance instance)
+{
+ return instance->driver_parameter;
+}
+
+int
+RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, NTP_Leap leap_status)
+{
+ double correction;
+ struct timeval cooked_time;
+ SRC_Instance inst = instance->source;
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Refclock, "refclock offset: %f", offset);
+#endif
+
+ SRC_SetReachable(inst);
+
+ correction = LCL_GetOffsetCorrection(sample_time);
+ UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time);
+
+ SRC_AccumulateSample(inst, &cooked_time, offset - correction + instance->offset,
+ 1e-6, 0.0, 0.0, 0.0, 0, leap_status);
+
+ instance->missed_samples = 0;
+
+ return 1;
+}
+
+static void
+poll_timeout(void *arg)
+{
+ double next;
+
+ RCL_Instance inst = (RCL_Instance)arg;
+
+ inst->missed_samples++;
+ inst->driver->poll(inst);
+
+ if (inst->missed_samples > 9)
+ SRC_UnsetReachable(inst->source);
+
+ if (inst->poll >= 0)
+ next = 1 << inst->poll;
+ else
+ next = 1.0 / (1 << -inst->poll);
+
+ inst->timeout_id = SCH_AddTimeoutByDelay(next, poll_timeout, arg);
+}
+
--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 2009
+ *
+ * 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.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for refclocks.
+
+ */
+
+#ifndef GOT_REFCLOCK_H
+#define GOT_REFCLOCK_H
+
+#include "srcparams.h"
+#include "sources.h"
+
+typedef struct {
+ char driver_name[4];
+ int driver_parameter;
+ int poll;
+ unsigned long ref_id;
+ double offset;
+} RefclockParameters;
+
+typedef struct RCL_Instance_Record *RCL_Instance;
+
+typedef struct {
+ int (*init)(RCL_Instance instance);
+ void (*fini)(RCL_Instance instance);
+ int (*poll)(RCL_Instance instance);
+} RefclockDriver;
+
+extern void RCL_Initialise(void);
+extern void RCL_Finalise(void);
+extern int RCL_AddRefclock(RefclockParameters *params);
+extern void RCL_StartRefclocks(void);
+extern void RCL_StartRefclocks(void);
+extern void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now);
+
+/* functions used by drivers */
+extern void RCL_SetDriverData(RCL_Instance instance, void *data);
+extern void *RCL_GetDriverData(RCL_Instance instance);
+extern int RCL_GetDriverParameter(RCL_Instance instance);
+extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, NTP_Leap leap_status);
+
+#endif
/* Flag indicating the status of the source */
SRC_Status status;
+ /* Type of the source */
+ SRC_Type type;
+
struct SelectInfo sel_info;
};
static void
slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, double afreq,
double doffset, int is_step_change, void *anything);
+static char *
+source_to_string(SRC_Instance inst);
/* ================================================== */
/* Initialisation function */
/* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */
-SRC_Instance SRC_CreateNewInstance(unsigned long ref_id)
+SRC_Instance SRC_CreateNewInstance(unsigned long ref_id, SRC_Type type)
{
SRC_Instance result;
result->ref_id = ref_id;
result->reachable = 0;
result->status = SRC_BAD_STATS;
+ result->type = type;
n_sources++;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
- UTI_IPToDottedQuad(inst->ref_id), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
+ source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
#endif
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
inst->reachable = 1;
#ifdef TRACEON
- LOG(LOGS_INFO, LOGF_Sources, "%s", UTI_IPToDottedQuad(inst->ref_id));
+ LOG(LOGS_INFO, LOGF_Sources, "%s", source_to_string(inst));
#endif
/* Don't do selection at this point, though - that will come about
inst->reachable = 0;
#ifdef TRACEON
- LOG(LOGS_INFO, LOGF_Sources, "%s%s", UTI_IPToDottedQuad(inst->ref_id),
+ LOG(LOGS_INFO, LOGF_Sources, "%s%s", source_to_string(inst),
(inst->index == selected_source_index) ? "(REF)":"");
#endif
}
}
+/* ================================================== */
+
+static char *
+source_to_string(SRC_Instance inst)
+{
+ switch (inst->type) {
+ case SRC_NTP:
+ return UTI_IPToDottedQuad(inst->ref_id);
+ case SRC_REFCLOCK:
+ return UTI_RefidToString(inst->ref_id);
+ default:
+ CROAK("Unknown source type");
+ }
+ return NULL;
+}
+
/* ================================================== */
/* This function selects the current reference from amongst the pool
of sources we are holding.
#if 0
LOG(LOGS_INFO, LOGF_Sources, "%s off=%f dist=%f lo=%f hi=%f",
- UTI_IPToDottedQuad(sources[i]->ref_id),
+ source_to_string(sources[i]),
si->best_offset, si->root_distance,
si->lo_limit, si->hi_limit);
#endif
for (i=0; i<n_endpoints; i++) {
#if 0
LOG(LOGS_INFO, LOGF_Sources, "i=%d t=%f tag=%d addr=%s", i, sort_list[i].offset, sort_list[i].tag,
- UTI_IPToDottedQuad(sources[sort_list[i].index]->ref_id));
+ source_to_string(sources[sort_list[i].index]));
#endif
switch(sort_list[i].tag) {
case LOW:
sel_sources[n_sel_sources++] = i;
#if 0
- LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is valid", i, UTI_IPToDottedQuad(sources[i]->ref_id));
+ LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is valid", i, source_to_string(sources[i]));
#endif
} else {
sources[i]->status = SRC_FALSETICKER;
#if 0
- LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is a falseticker", i, UTI_IPToDottedQuad(sources[i]->ref_id));
+ LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is a falseticker", i, source_to_string(sources[i]));
#endif
}
}
sel_sources[i] = INVALID_SOURCE;
sources[index]->status = SRC_JITTERY;
#if 0
- LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s has too much variance", i, UTI_IPToDottedQuad(sources[i]->ref_id));
+ LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s has too much variance", i, source_to_string(sources[i]));
#endif
}
}
selected_source_index = min_distance_index;
LOG(LOGS_INFO, LOGF_Sources, "Selected source %s",
- UTI_IPToDottedQuad(sources[selected_source_index]->ref_id));
+ source_to_string(sources[selected_source_index]));
#if 0
/* ================================================== */
+SRC_Type
+SRC_GetType(int index)
+{
+ if ((index >= n_sources) || (index < 0))
+ return -1;
+ return sources[index]->type;
+}
+
+/* ================================================== */
+
SRC_Skew_Direction SRC_LastSkewChange(SRC_Instance inst)
{
SRC_Skew_Direction result = SRC_Skew_Nochange;
/* Finalisation function */
extern void SRC_Finalise(void);
+typedef enum {
+ SRC_NTP, /* NTP client/peer */
+ SRC_REFCLOCK /* Rerefence clock */
+} SRC_Type;
+
/* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */
-extern SRC_Instance SRC_CreateNewInstance(unsigned long ref_id);
+extern SRC_Instance SRC_CreateNewInstance(unsigned long ref_id, SRC_Type type);
/* Function to get rid of a source when it is being unconfigured.
This may cause the current reference source to be reselected, if this
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report);
+extern SRC_Type SRC_GetType(int index);
+
typedef enum {
SRC_Skew_Decrease,
SRC_Skew_Nochange,
/* ================================================== */
+char *
+UTI_RefidToString(unsigned long ref_id)
+{
+ unsigned int a, b, c, d;
+ char *result;
+ a = (ref_id>>24) & 0xff;
+ b = (ref_id>>16) & 0xff;
+ c = (ref_id>> 8) & 0xff;
+ d = (ref_id>> 0) & 0xff;
+ result = NEXT_BUFFER;
+ snprintf(result, BUFFER_LENGTH, "%c%c%c%c", a, b, c, d);
+ return result;
+}
+
+/* ================================================== */
+
char *
UTI_IPToDottedQuad(unsigned long ip)
{
diagnostic display */
extern char *UTI_TimestampToString(NTP_int64 *ts);
+/* Convert ref_id into a temporary string, for diagnostics */
+extern char *UTI_RefidToString(unsigned long ref_id);
+
/* Convert an IP address to dotted quad notation, for diagnostics */
extern char *UTI_IPToDottedQuad(unsigned long ip);