diff --git a/Makefile.in b/Makefile.in index 6550543..8058648 100644 --- a/Makefile.in +++ b/Makefile.in @@ -41,7 +41,7 @@ OBJS = util.o sched.o regress.o local.o \ 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@ diff --git a/client.c b/client.c index 54b640f..b4f65f0 100644 --- a/client.c +++ b/client.c @@ -92,6 +92,19 @@ time_to_log_form(time_t t) /* ================================================== */ +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) { @@ -1462,7 +1475,9 @@ process_cmd_sources(char *line) 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); diff --git a/cmdmon.c b/cmdmon.c index 819977c..e2d2ded 100644 --- a/cmdmon.c +++ b/cmdmon.c @@ -50,6 +50,7 @@ #include "rtc.h" #include "pktlength.h" #include "clientlog.h" +#include "refclock.h" /* ================================================== */ @@ -871,7 +872,14 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message) /* 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); diff --git a/conf.c b/conf.c index 06ff104..9914413 100644 --- a/conf.c +++ b/conf.c @@ -44,6 +44,7 @@ #include "conf.h" #include "ntp_sources.h" #include "ntp_core.h" +#include "refclock.h" #include "cmdmon.h" #include "srcparams.h" #include "logging.h" @@ -73,6 +74,7 @@ static void parse_peer(const char *); 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 *); @@ -187,6 +189,7 @@ typedef struct { 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}, @@ -250,6 +253,11 @@ typedef struct { 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 { @@ -417,6 +425,61 @@ parse_peer(const char *line) /* ================================================== */ +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) { @@ -1004,6 +1067,17 @@ CNF_AddSources(void) { /* ================================================== */ +void +CNF_AddRefclocks(void) { + int i; + + for (i=0; i 0) { SYS_SetScheduler(SchedPriority); diff --git a/ntp_core.c b/ntp_core.c index d2f67cb..4a33f72 100644 --- a/ntp_core.c +++ b/ntp_core.c @@ -319,7 +319,7 @@ create_instance(NTP_Remote_Address *remote_addr, NTP_Mode mode, SourceParameters 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; diff --git a/refclock.c b/refclock.c new file mode 100644 index 0000000..372767a --- /dev/null +++ b/refclock.c @@ -0,0 +1,213 @@ +/* + 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); +} + diff --git a/refclock.h b/refclock.h new file mode 100644 index 0000000..4260616 --- /dev/null +++ b/refclock.h @@ -0,0 +1,63 @@ +/* + 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 diff --git a/sources.c b/sources.c index d90eb99..db8795b 100644 --- a/sources.c +++ b/sources.c @@ -96,6 +96,9 @@ struct SRC_Instance_Record { /* Flag indicating the status of the source */ SRC_Status status; + /* Type of the source */ + SRC_Type type; + struct SelectInfo sel_info; }; @@ -126,6 +129,8 @@ static int selected_source_index; /* Which source index is currently 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 */ @@ -155,7 +160,7 @@ void SRC_Finalise(void) /* 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; @@ -186,6 +191,7 @@ SRC_Instance SRC_CreateNewInstance(unsigned long ref_id) result->ref_id = ref_id; result->reachable = 0; result->status = SRC_BAD_STATS; + result->type = type; n_sources++; @@ -280,7 +286,7 @@ void SRC_AccumulateSample #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 @@ -301,7 +307,7 @@ SRC_SetReachable(SRC_Instance inst) 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 @@ -316,7 +322,7 @@ SRC_UnsetReachable(SRC_Instance inst) 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 @@ -349,6 +355,22 @@ compare_sort_elements(const void *a, const void *b) } } +/* ================================================== */ + +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. @@ -418,7 +440,7 @@ SRC_SelectSource(unsigned long match_addr) #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 @@ -491,7 +513,7 @@ SRC_SelectSource(unsigned long match_addr) for (i=0; iref_id)); + source_to_string(sources[sort_list[i].index])); #endif switch(sort_list[i].tag) { case LOW: @@ -565,12 +587,12 @@ SRC_SelectSource(unsigned long match_addr) 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 } } @@ -603,7 +625,7 @@ SRC_SelectSource(unsigned long match_addr) 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 } } @@ -658,7 +680,7 @@ SRC_SelectSource(unsigned long match_addr) 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 @@ -935,6 +957,16 @@ SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report) /* ================================================== */ +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; diff --git a/sources.h b/sources.h index c895cac..3afd806 100644 --- a/sources.h +++ b/sources.h @@ -50,10 +50,15 @@ extern void SRC_Initialise(void); /* 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 @@ -143,6 +148,8 @@ extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report); +extern SRC_Type SRC_GetType(int index); + typedef enum { SRC_Skew_Decrease, SRC_Skew_Nochange, diff --git a/util.c b/util.c index d506ffd..30cbd48 100644 --- a/util.c +++ b/util.c @@ -247,6 +247,22 @@ UTI_TimestampToString(NTP_int64 *ts) /* ================================================== */ +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) { diff --git a/util.h b/util.h index 7ab9bb8..7ec7d2c 100644 --- a/util.h +++ b/util.h @@ -72,6 +72,9 @@ extern char *UTI_TimevalToString(struct timeval *tv); 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);