1
0
mirror of https://frontier.innolan.net/rainlance/amiga-ntimed.git synced 2025-11-22 11:52:41 +00:00

First preview release.

This commit is contained in:
Poul-Henning Kamp
2014-12-21 22:31:18 +00:00
commit 45af90e98a
30 changed files with 4858 additions and 0 deletions

218
README.rst Normal file
View File

@ -0,0 +1,218 @@
Ntimed -- Network Time Synchronization
======================================
What is this ?
~~~~~~~~~~~~~~
This is a preview/early-acces/alpha/buzzword-of-the-times release
of a new FOSS project written to gradually take over the world of
networked timekeeping.
The first step is a NTP protocol client daemon, 'Ntimed-client',
which will synchronize a systems clock to some set of NTP servers
If this catches on, support for slave servers, refclocks and other
protocols, such as PTP, can be added, subject to interest, skill,
time and money.
The overall architectural goals are the same as every other FOSS
project claims to follow: Simplicity, Quality, Security etc. etc.
but I tend to think that we stick a little bit more closely to them.
This work is sponsored by Linux Foundation, partly in response to
the HeartBleed fiasco, and after studying the 300,000+ lines of
source-code in NTPD, I concluded that while it *could* be salvaged,
it would be more economical, much faster and far more efficient to
start from scratch.
This is the result.
What should you do with this
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can take this code, compile it, run it, and it will steer your
computers clock, but I am not going to encourage you to do that in
production yet -- unless you know what you are doing and why you
are doing it: This is only a preview release.
Soon-ish, there will be full production-ready releases and
packages for your favourite operating system, but we are not
there yet.
But if you are willing to read C-source code to figure out what the
printouts mean, if you care about quality time-keeping or quality
programming, I would love to hear your feedback, reviews and ideas.
Where can I read more ?
~~~~~~~~~~~~~~~~~~~~~~~
I maintain a blog-of-sorts about this project here:
http://phk.freebsd.dk/time
There you will find information about theory, practice
and the thinking that tries to bridge the gap between them.
Updates typically happen during weekends -- that is when I work on
Ntimed.
Who do I yell at, and how ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Me, Poul-Henning Kamp. Please send email to 'phk@Ntimed.org'.
What happens next ?
~~~~~~~~~~~~~~~~~~~
The plan is to have the first production ready release in Q1/2015.
Hopefully OS releases will then adopt Ntimed, first as alternative
to, and later as replacement for NTPD in client applications.
It is not my intent to start and manage an entirely new FOSS project
around Ntimed, Harlan from The Network Time Foundation has agreed
to adopt Ntimed and it will run in/with/parallel to the NTPD project.
Or something. We still need to flesh out all those details.
How to compile
~~~~~~~~~~~~~~
Pull the source code over to the machine you want to play on, and::
sh configure
make
(That was fast, wasn't it ? -- Amazing how slow things arean't
when you don't go hunting for 27 different FORTRAN compilers for
your C code.)
How to test
~~~~~~~~~~~
Stop ntpd if it is running, and then::
./ntimed-client -t /tmp/somefile some_ntp_server some_other_ntp_server
That should synchronize your clock to those servers.
Because this is a preview release, the process will not "daemonize"
into the background.
The '-t /tmp/somefile' arguments tells it to write a full blow-by-blow
tracefile, for analysis and debugging.
If something goes wrong, I'm going to ask you for a copy the tracefile.
What happens when you run it ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After a few seconds, your clock will be stepped if necessary.
(In the final version, this is where the process daemonizes into the
background -- from which point you can trust your clock to be good.)
In the next 30-60 seconds, the PLL will eliminate any residual phase
error and from this point in time, your computers clock should be
good to a few milliseconds, depending on the quality of the servers.
After about 5-10 minutes, the PLL will have integrated the
frequency error of your computers crystal, and the PLL will
start to "stiffen" to minimize the amount of steering necessary
to keep the clock aligned to the servers.
If you are using distant or very distant servers, things will
take longer.
Packet traces and simulations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can also run the program two other ways::
./Ntimed-client --poll-server some_ntp_server some_other_ntp_server
This will *not* steer your clock, but it will query the servers as
if it should have steered your clock. (Ntpd should *not* be running
at the same time.) By default it terminates after 1800 seconds,
but you can control that with "-d 3600" for one hour etc.
If you save the output into a file (redirect stdout or use '-t filename'),
you can use it as input for a simulation run::
./Ntimed-client --sim-client -s filename -t /tmp/_
And then run::
python plotgen.py
Then you can finally get some nice pictures to look at by::
gnuplot
load '/tmp/_g'
Tweaking parameters
~~~~~~~~~~~~~~~~~~~
Parameters can be examined and tweaked with '-p' arguments::
-p '?'
Gives a list of available parameters, and you can get information about
each parameter::
-p parameter_name
To set the parameter to a non-default value::
-p parameter_name=new_value
Not everything which should be a parameter is yet, and there are
some unused dummy parameters there, just to make sure the macro-magic
works.
Thanks and acknowledegments
~~~~~~~~~~~~~~~~~~~~~~~~~~~
First and foremost a big thanks to Professor Dave L. Mills.
Thanks for being the first time-nut on the InterNETs, as we called
them back then.
Thanks for being an all-round pleasant fellow to work with.
Thanks for adopting my 'nanokernel' and 'refclock_oncore'.
But in particular thanks for lending me the most cantankerous LORAN-C
receiver the world have ever seen, at a time in my life where I
badly needed that a distraction to keep me sane.
A big thanks to the Linux Foundation for realizing that NTPD was
in dire straits after Dave Mills retired.
Thanks for giving me money and free hands to do what I thought was
best -- even though I am a "BSD-guy".
Thanks to Harlan Steen for keeping the NTPD flame burning, however
stormy the last decade has been.
I trust The Network Time Foundation will take as good care of Ntimed
in the future, as it has taken care of NTPD in the past.
A special wave of the hat to John R. Vig for his famous Quartz
Crystal Tutorial.
And finally, a shout-out and thanks to time-nuts@ in general and
Tom Van Baak in particular, for being jolly and interesting company
for people who happen to care about nanoseconds, leap seconds,
choke-ring antennas and the finer points of SC- vs. AT-cut quartz
crystals.
*phk*

216
combine_delta.c Normal file
View File

@ -0,0 +1,216 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Source Combiner based on delta-pdfs
* ===================================
*
* The basic principle here is that sources gives us four values:
* - The highest low value were the probability is zero.
* - The lowest high value were the probability is zero.
* - The most probable value
* - The relative trust in that value [0...1]
* Together this defines a triangular probability density function.
*
* The combiner adds all these pdfs' together weighted by trust
* and finds the highest probability which sports a quorum.
*
* See also: http://phk.freebsd.dk/time/20141107.html
*
* XXX: decay trust by age
* XXX: auto determine quorom if param not set
* XXX: param for minimum probability density
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "ntimed.h"
struct combine_delta;
struct cd_stat {
double x;
double prob;
unsigned quorum;
};
struct cd_source {
unsigned magic;
#define CD_SOURCE_MAGIC 0x2799775c
TAILQ_ENTRY(cd_source) list;
struct combiner combiner;
struct combine_delta *cd;
double trust, low, mid, high;
int tb_gen;
};
struct combine_delta {
unsigned magic;
#define COMBINE_DELTA_MAGIC 0x8dc5030c
unsigned nsrc;
TAILQ_HEAD(, cd_source) head;
};
struct combine_delta *
CD_New(void)
{
struct combine_delta *cd;
ALLOC_OBJ(cd, COMBINE_DELTA_MAGIC);
AN(cd);
TAILQ_INIT(&cd->head);
return (cd);
}
static void
cd_try_peak(const struct combine_delta *cd, double *mx, double *my, double x,
struct cd_stat *st)
{
struct cd_source *cs;
// XXX: Hack to make plots with log zscale and only one
// XXX: source look sensible.
st->x = x;
st->prob = 0.001;
st->quorum = 0;
TAILQ_FOREACH(cs, &cd->head, list) {
if (cs->tb_gen != TB_generation)
continue;
if (x < cs->low)
continue;
if (x > cs->high)
continue;
if (cs->low >= cs->high)
continue;
st->quorum++;
if (x < cs->mid) {
st->prob += cs->trust * 2.0 * (x - cs->low) /
((cs->high - cs->low) * (cs->mid - cs->low));
} else {
st->prob += cs->trust * 2.0 * (cs->high - x) /
((cs->high - cs->low) * (cs->high - cs->mid));
}
if (isnan(st->prob)) {
Fail(NULL, 0, "lo %.3e hi %.3e mid %.3e",
cs->low, cs->high, cs->mid);
}
}
if (st->prob > *my) {
*my = st->prob;
*mx = x;
}
}
static int
stat_cmp(const void *p1, const void *p2)
{
const struct cd_stat *left = p1;
const struct cd_stat *right = p2;
/*lint -save -e514 */
return ((left->x > right->x) - (left->x < right->x));
/*lint -restore */
}
static void
cd_find_peak(struct ocx *ocx, const struct combine_delta *cd)
{
struct cd_source *cs;
double max_x = 0;
double max_y = 1;
struct cd_stat st[cd->nsrc * 3L];
size_t m;
m = 0;
TAILQ_FOREACH(cs, &cd->head, list) {
if (cs->tb_gen != TB_generation)
continue;
cd_try_peak(cd, &max_x, &max_y, cs->low, &st[m++]);
cd_try_peak(cd, &max_x, &max_y, cs->mid, &st[m++]);
cd_try_peak(cd, &max_x, &max_y, cs->high, &st[m++]);
}
Put(ocx, OCX_TRACE,
" %.3e %.3e %.3e\n", max_x, max_y, log(max_y)/log(10.));
PLL(ocx, max_x, max_y);
qsort(st, cd->nsrc * 3L, sizeof st[0], stat_cmp);
}
static void __match_proto__(combine_f)
cd_filter(struct ocx *ocx, const struct combiner *cb,
double trust, double low, double mid, double high)
{
struct cd_source *cs;
struct combine_delta *cd;
CHECK_OBJ_NOTNULL(cb, COMBINER_MAGIC);
CAST_OBJ_NOTNULL(cs, cb->priv, CD_SOURCE_MAGIC);
cd = cs->cd;
CHECK_OBJ_NOTNULL(cd, COMBINE_DELTA_MAGIC);
/* Sign: local - remote -> postive is ahead */
assert(trust >= 0 && trust <= 1.0);
cs->trust = trust;
cs->low = low;
cs->mid = mid;
cs->high = high;
cs->tb_gen = TB_generation;
Put(ocx, OCX_TRACE,
"Combine %s %s %.6f %.6f %.6f", cb->name1, cb->name2,
cs->low, cs->mid, cs->high);
cd_find_peak(ocx, cd);
}
struct combiner *
CD_AddSource(struct combine_delta *cd, const char *name1, const char *name2)
{
struct cd_source *cs;
CHECK_OBJ_NOTNULL(cd, COMBINE_DELTA_MAGIC);
ALLOC_OBJ(cs, CD_SOURCE_MAGIC);
AN(cs);
cs->cd = cd;
cs->low = cs->mid = cs->high = NAN;
TAILQ_INSERT_TAIL(&cd->head, cs, list);
INIT_OBJ(&cs->combiner, COMBINER_MAGIC);
cs->combiner.func = cd_filter;
cs->combiner.priv = cs;
cs->combiner.name1 = name1;
cs->combiner.name2 = name2;
cd->nsrc += 1;
return (&cs->combiner);
}

144
configure vendored Normal file
View File

@ -0,0 +1,144 @@
#!/bin/sh
#
# Copyright (c) 2014 Poul-Henning Kamp
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# Handwritten configure script.
# =============================
#
# Before you suggest I use tools for this, please read:
#
# https://www.varnish-cache.org/docs/trunk/phk/autocrap.html
# and
# http://queue.acm.org/detail.cfm?id=2349257
#
set -e
# List of .h files
# NB: These SHALL always be included with #include "..."
HDRS='
ntimed.h
ntimed_endian.h
ntimed_queue.h
ntimed_tricks.h
ntp.h
ntp_tbl.h
param_instance.h
param_tbl.h
udp.h
'
# List of .c files
SRCS='
combine_delta.c
main.c
main_client.c
main_poll_server.c
main_sim_client.c
ntp_filter.c
ntp_packet.c
ntp_peer.c
ntp_peerset.c
ntp_tools.c
ocx_stdio.c
param.c
pll_std.c
time_sim.c
time_stuff.c
time_unix.c
todo.c
udp.c
'
if [ -f /usr/share/mk/bsd.prog.mk ] ; then
echo "Found bsd.prog.mk, will use it."
(
echo '# BSD-style Makefile generated by configure'
echo 'PROG = ntimed-client'
for f in ${SRCS}
do
echo "SRCS += ${f}"
done
echo 'NO_MAN = not_yet'
echo 'LDADD += -lm'
echo 'WARNS ?= 6'
echo '.include <bsd.prog.mk>'
) > Makefile
msg=", remeber to run 'make depend'"
else
(
echo '# Portable Makefile generated by configure'
echo ''
echo 'all: ntimed-client'
echo ''
echo "CFLAGS += -Wall -Werror"
echo ''
for f in ${HDRS}
do
b=`basename $f .h`
i=`sed -n -e '/#include.*"/{
s/"$//
s/.*"//
p
}' $f | sort -u`
if [ "x${i}" != "x" ] ; then
echo "${b}.h: " ${i}
echo " touch ${b}.h"
echo
fi
done
l=""
for f in ${SRCS}
do
b=`basename $f .c`
i=`sed -n -e '/#include.*"/{
s/"$//
s/.*"//
p
}' $f | sort -u`
echo "${b}.o: ${b}.c" ${i}
echo
l="${l} ${b}.o"
done
echo
echo "ntimed-client: ${l}"
echo " \${CC} \${CFLAGS} -o ntimed-client ${l} -lm"
echo
echo "clean:"
echo " rm -f ${l} ntimed-client"
echo
echo "depend:"
echo " @echo Dependencies already done"
) > Makefile
fi
echo "Makefile generated${msg}"

74
main.c Normal file
View File

@ -0,0 +1,74 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Main main() functions
* =====================
*
*/
#include <string.h>
#include "ntimed.h"
#include "ntp.h"
/*************************************************************************/
static void
dummy(void)
{
// Reference otherwise unused "library" functions
NTP_Peer_Destroy(NULL);
}
static int
main_run_tests(int argc, char * const * argv)
{
(void)argc;
(void)argv;
Time_Unix_Passive();
TS_RunTest(NULL);
return (0);
}
int
main(int argc, char * const *argv)
{
if (getpid() == 0)
dummy();
if (argc > 1 && !strcmp(argv[1], "--poll-server"))
return (main_poll_server(argc - 1, argv + 1));
if (argc > 1 && !strcmp(argv[1], "--sim-client"))
return (main_sim_client(argc - 1, argv + 1));
if (argc > 1 && !strcmp(argv[1], "--run-tests"))
return (main_run_tests(argc - 1, argv + 1));
return (main_client(argc, argv));
}

115
main_client.c Normal file
View File

@ -0,0 +1,115 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Client main function
* ====================
*
* Steer system time based on NTP servers
*
*/
#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include "ntimed.h"
#include "ntp.h"
#include "udp.h"
#define PARAM_CLIENT PARAM_INSTANCE
#define PARAM_TABLE_NAME client_param_table
#include "param_instance.h"
#undef PARAM_TABLE_NAME
#undef PARAM_CLIENT
int
main_client(int argc, char *const *argv)
{
int ch;
struct ntp_peer *np;
struct ntp_peerset *npl;
struct todolist *tdl;
struct combine_delta *cd;
int fd;
int npeer = 0;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
tdl = TODO_NewList();
Time_Unix(tdl);
PLL_Init();
npl = NTP_PeerSet_New(NULL);
Param_Register(client_param_table);
NF_Init();
while ((ch = getopt(argc, argv, "p:t:")) != -1) {
switch(ch) {
case 'p':
Param_Tweak(NULL, optarg);
break;
case 't':
ArgTracefile(optarg);
break;
default:
Fail(NULL, 0,
"Usage %s [-p param] [-t tracefile] servers...",
argv[0]);
break;
}
}
argc -= optind;
argv += optind;
for (ch = 0; ch < argc; ch++)
npeer += NTP_PeerSet_Add(NULL, npl, argv[ch]);
if (npeer == 0)
Fail(NULL, 0, "No NTP peers found");
Put(NULL, OCX_TRACE, "# NTIMED Format client 1.0\n");
Put(NULL, OCX_TRACE, "# Found %d peers\n", npeer);
Param_Report(NULL, OCX_TRACE);
fd = UdpTimedSocket(NULL, AF_INET);
if (fd < 0)
Fail(NULL, errno, "Could not open UDP socket");
cd = CD_New();
NTP_PeerSet_Foreach(np, npl) {
NF_New(np);
np->combiner = CD_AddSource(cd, np->hostname, np->ip);
}
NTP_PeerSet_Poll(NULL, npl, fd, tdl);
(void)TODO_Run(NULL, tdl);
return (0);
}

160
main_poll_server.c Normal file
View File

@ -0,0 +1,160 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* poll-server
* [-d duration] When to stop
* [-m monitor] Poll this monitor every 32 seconds
* [-t tracefile] Where to save the output (if not stdout)
* server ... What servers to poll
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include "ntimed.h"
#include "ntp.h"
#include "udp.h"
static int fd;
static void
mps_filter(struct ocx *ocx, const struct ntp_peer *np)
{
char buf[256];
NTP_Tool_Format(buf, sizeof buf, np->rx_pkt);
Put(ocx, OCX_TRACE, "Poll %s %s %s\n", np->hostname, np->ip, buf);
}
static enum todo_e __match_proto__(todo_f)
mps_mon(struct ocx *ocx, struct todolist *tdl, void *priv)
{
char buf[256];
struct ntp_peer *np;
int i;
(void)ocx;
(void)tdl;
CAST_OBJ_NOTNULL(np, priv, NTP_PEER_MAGIC);
i = NTP_Peer_Poll(ocx, fd, np, 0.2);
if (i == 1) {
NTP_Tool_Format(buf, sizeof buf, np->rx_pkt);
Put(ocx, OCX_TRACE,
"Monitor %s %s %s\n", np->hostname, np->ip, buf);
} else {
Put(ocx, OCX_TRACE,
"Monitor_err %s %s %d\n", np->hostname, np->ip, i);
}
return(TODO_OK);
}
static enum todo_e __match_proto__(todo_f)
mps_end(struct ocx *ocx, struct todolist *tdl, void *priv)
{
(void)tdl;
(void)priv;
Put(ocx, OCX_TRACE, "# Run completed\n");
return(TODO_FAIL);
}
int
main_poll_server(int argc, char *const *argv)
{
int ch;
int npeer = 0;
char *p;
struct ntp_peerset *npl;
struct ntp_peer *mon = NULL;
struct ntp_peer *np;
struct todolist *tdl;
double duration = 1800;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
ArgTracefile("-");
tdl = TODO_NewList();
Time_Unix_Passive();
npl = NTP_PeerSet_New(NULL);
AN(npl);
while ((ch = getopt(argc, argv, "d:m:t:")) != -1) {
switch(ch) {
case 'd':
duration = strtod(optarg, &p);
if (*p != '\0' || duration < 1.0)
Fail(NULL, 0, "Invalid -d argument");
break;
case 'm':
mon = NTP_Peer_NewLookup(NULL, optarg);
if (mon == NULL)
Fail(NULL, 0, "Monitor (-m) didn't resolve.");
break;
case 't':
ArgTracefile(optarg);
break;
default:
Fail(NULL, 0,
"Usage %s [-d duration] [-m monitor] "
"[-t tracefile] server...", argv[0]);
break;
}
}
argc -= optind;
argv += optind;
for (ch = 0; ch < argc; ch++)
npeer += NTP_PeerSet_Add(NULL, npl, argv[ch]);
Put(NULL, OCX_TRACE, "# NTIMED Format poll-server 1.0\n");
Put(NULL, OCX_TRACE, "# Found %d peers\n", npeer);
if (npeer == 0)
Fail(NULL, 0, "No peers found");
NTP_PeerSet_Foreach(np, npl) {
Put(NULL, OCX_TRACE, "# Peer %s %s\n", np->hostname, np->ip);
np->filter_func = mps_filter;
}
if (mon != NULL)
Put(NULL, OCX_TRACE,
"# Monitor %s %s\n", mon->hostname, mon->ip);
fd = UdpTimedSocket(NULL, AF_INET);
assert(fd >= 0);
TODO_ScheduleRel(tdl, mps_end, NULL, duration, 0, "End task");
if (mon != NULL)
TODO_ScheduleRel(tdl, mps_mon, mon, 0, 32, "Monitor");
NTP_PeerSet_Poll(NULL, npl, fd, tdl);
(void)TODO_Run(NULL, tdl);
return (0);
}

297
main_sim_client.c Normal file
View File

@ -0,0 +1,297 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* sim_client
* -s simfile Output file from poll-server
* server_numbers ...
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ntimed.h"
#include "ntp.h"
#define PARAM_CLIENT PARAM_INSTANCE
#define PARAM_TABLE_NAME client_param_table
#include "param_instance.h"
#undef PARAM_TABLE_NAME
#undef PARAM_CLIENT
/**********************************************************************/
struct sim_file {
unsigned magic;
#define SIM_FILE_MAGIC 0x7f847bd0
char *filename;
FILE *input;
unsigned n_peer;
struct ntp_peerset *npl;
struct timestamp when;
unsigned t0;
};
static void
simfile_poll(struct ocx *ocx, const struct sim_file *sf, char *buf)
{
char *hostname;
char *ip;
char *pkt;
struct ntp_peer *np;
struct ntp_packet *rxp;
struct ntp_packet *txp;
CHECK_OBJ_NOTNULL(sf, SIM_FILE_MAGIC);
AN(buf);
if (memcmp(buf, "Poll ", 5))
Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf);
hostname = buf + 5;
ip = strchr(hostname, ' ');
if (ip == NULL)
Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf);
pkt = strchr(ip + 1, ' ');
if (pkt == NULL)
Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf);
*ip++ = '\0';
*pkt++ = '\0';
NTP_PeerSet_Foreach(np, sf->npl)
if (!strcmp(np->hostname, hostname) && !strcmp(np->ip, ip))
break;
if (np == NULL)
Fail(ocx, 0, "Peer not found (%s, %s)\n", hostname, ip);
CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC);
txp = np->tx_pkt;
INIT_OBJ(txp, NTP_PACKET_MAGIC);
rxp = np->rx_pkt;
if (NTP_Tool_Scan(rxp, pkt))
Fail(ocx, 0, "Cannot parse packet (%s, %s, %s)\n",
hostname, ip, pkt);
TS_Add(&rxp->ntp_origin, Time_Sim_delta);
TS_Add(&rxp->ts_rx, Time_Sim_delta);
txp->ntp_transmit = rxp->ntp_origin;
if (np->filter_func != NULL)
np->filter_func(ocx, np);
}
static enum todo_e
simfile_readline(struct ocx *ocx, struct todolist *tdl, void *priv)
{
struct sim_file *sf;
char buf[BUFSIZ], *p;
struct timestamp t0;
unsigned u1, u2;
double dt;
AN(tdl);
CAST_OBJ_NOTNULL(sf, priv, SIM_FILE_MAGIC);
TB_Now(&t0);
while (1) {
if (fgets(buf, sizeof buf, sf->input) == NULL) {
Debug(ocx, "EOF on -s file (%s)", sf->filename);
exit(0);
}
p = strchr(buf, '\r');
if (p != NULL)
*p = '\0';
p = strchr(buf, '\n');
if (p != NULL)
*p = '\0';
if (!strncmp(buf, "Now ", 4)) {
if (sscanf(buf, "Now %u.%u", &u1, &u2) != 2)
Fail(ocx, 0, "Bad 'Now' line (%s)", buf);
if (sf->t0 == 0)
sf->t0 = u1 - t0.sec;
u1 -= sf->t0;
TS_Nanosec(&sf->when, u1, u2);
dt = TS_Diff(&sf->when, &t0);
if (dt >= 1e-3) {
TODO_ScheduleAbs(tdl, simfile_readline, priv,
&sf->when, 0.0, "Readline");
return (TODO_OK);
}
} else if (!strncmp(buf, "Poll ", 5)) {
simfile_poll(ocx, sf, buf);
}
/* We ignore things we don't understand */
}
}
static struct sim_file *
SimFile_Open(struct ocx *ocx, const char *fn, struct todolist *tdl,
struct ntp_peerset *npl)
{
struct sim_file *sf;
struct ntp_peer *np;
char buf[BUFSIZ];
char buf2[BUFSIZ];
char buf3[BUFSIZ];
char *e;
int s;
unsigned fpeer = 0;
AN(fn);
AN(tdl);
AN(npl);
ALLOC_OBJ(sf, SIM_FILE_MAGIC);
AN(sf);
sf->input = fopen(fn, "r");
if (sf->input == NULL)
Fail(ocx, 1, "Could not open -s file (%s)", fn);
sf->filename = strdup(fn);
AN(sf->filename);
sf->npl = npl;
for (s = 0; s < 3; ) {
if (fgets(buf, sizeof buf, sf->input) == NULL)
Fail(ocx, 1, "Premature EOF on -s file (%s)", fn);
e = strchr(buf, '\0');
AN(e);
if (e == buf)
continue;
if (e[-1] == '\n')
*--e = '\0';
Debug(ocx, ">>> %s\n", buf);
switch(s) {
case 0:
if (strcmp(buf, "# NTIMED Format poll-server 1.0"))
Fail(ocx, 0,
"Wrong fileformat in -s file (%s)", fn);
s++;
break;
case 1:
if (sscanf(buf, "# Found %u peers", &sf->n_peer) != 1)
Fail(ocx, 0,
"Expected '# Found ... peers' line");
s++;
break;
case 2:
if (sscanf(buf, "# Peer %s %s", buf2, buf3) != 2)
Fail(ocx, 0, "Expected '# Peer' line", fpeer);
np = NTP_Peer_NewLookup(ocx, buf3);
AN(np);
np->hostname = strdup(buf2);
AN(np->hostname);
NTP_PeerSet_AddPeer(ocx, npl, np);
if (++fpeer == sf->n_peer)
s++;
break;
default:
Debug(ocx, "<%s>\n", buf);
Fail(ocx, 0,
"XXX: Wrong state (%d) in open_sim_file", s);
}
}
(void)simfile_readline(NULL, tdl, sf);
return (sf);
}
int
main_sim_client(int argc, char *const *argv)
{
int ch;
const char *s_filename = NULL;
struct sim_file *sf;
struct ntp_peerset *npl;
struct ntp_peer *np;
struct todolist *tdl;
struct combine_delta *cd;
double a, b, c;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
tdl = TODO_NewList();
Time_Sim(tdl);
PLL_Init();
npl = NTP_PeerSet_New(NULL);
Param_Register(client_param_table);
NF_Init();
while ((ch = getopt(argc, argv, "B:s:p:t:")) != -1) {
switch(ch) {
case 'B':
ch = sscanf(optarg, "%lg,%lg,%lg", &a, &b, &c);
if (ch != 3)
Fail(NULL, 0,
"bad -B argument \"when,freq,phase\"");
Time_Sim_Bump(tdl, a, b, c);
break;
case 's':
s_filename = optarg;
break;
case 'p':
Param_Tweak(NULL, optarg);
break;
case 't':
ArgTracefile(optarg);
break;
default:
Fail(NULL, 0,
"Usage %s [-s simfile] [-p params] [-t tracefile]"
" [-B when,freq,phase]", argv[0]);
break;
}
}
// argc -= optind;
// argv += optind;
Param_Report(NULL, OCX_TRACE);
if (s_filename == NULL)
Fail(NULL, 1, "You must specify -s file.");
sf = SimFile_Open(NULL, s_filename, tdl, npl);
AN(sf);
cd = CD_New();
NTP_PeerSet_Foreach(np, npl) {
NF_New(np);
np->combiner = CD_AddSource(cd, np->hostname, np->ip);
}
(void)TODO_Run(NULL, tdl);
return (0);
}

179
ntimed.h Normal file
View File

@ -0,0 +1,179 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Main include file
* =================
*/
#ifdef NTIMED_H_INCLUDED
#error "ntimed.h included multiple times"
#endif
#define NTIMED_H_INCLUDED
#include <stdint.h>
#include <unistd.h>
#include "ntimed_queue.h"
#include "ntimed_tricks.h"
struct todolist;
/* ocx_*.c -- Operational Context *************************************/
struct ocx; // private
enum ocx_chan {
OCX_DIAG, // think: stderr
OCX_TRACE, // think: /var/run/stats
OCX_DEBUG, // think: stdout
};
void Put(struct ocx *, enum ocx_chan, const char *, ...);
void PutHex(struct ocx *, enum ocx_chan, const void *, ssize_t len);
/*
* Report "Failure: " + args + "\n" [+ errno-line] + "\n". exit(1);
*/
void Fail(struct ocx *, int err, const char *, ...) \
__attribute__((__noreturn__));
#define Debug(ocx, ...) Put(ocx, OCX_DEBUG, __VA_ARGS__)
#define DebugHex(ocx, ptr, len) PutHex(ocx, OCX_DEBUG, ptr, len)
void ArgTracefile(const char *fn);
/* param.c -- Parameters **********************************************/
struct param_tbl {
const char *name;
double *val;
double min;
double max;
double def;
const char *doc;
TAILQ_ENTRY(param_tbl) list;
};
void Param_Register(struct param_tbl *pt);
void Param_Tweak(struct ocx *, const char *arg);
void Param_Report(struct ocx *ocx, enum ocx_chan);
/* pll_std.c -- Standard PLL ******************************************/
typedef void pll_f(struct ocx *ocx, double offset, double weight);
extern pll_f *PLL;
void PLL_Init(void);
/* time_sim.c -- Simulated timebase ***********************************/
extern double Time_Sim_delta;
void Time_Sim(struct todolist *);
void Time_Sim_Bump(struct todolist *, double when, double freq, double phase);
/* time_unix.c -- UNIX timebase ***************************************/
void Time_Unix(struct todolist *);
void Time_Unix_Passive(void);
/* time_stuff.c -- Timebase infrastructure ****************************/
struct timestamp {
unsigned magic;
#define TIMESTAMP_MAGIC 0x344cd213
uint64_t sec; // Really: time_t
uint64_t frac;
};
typedef void tb_sleep_f(double dur);
typedef struct timestamp *tb_now_f(struct timestamp *);
typedef void tb_step_f(struct ocx *, double offset);
typedef void tb_adjust_f(struct ocx *, double offset, double duration,
double frequency);
extern int TB_generation;
extern tb_sleep_f *TB_Sleep;
extern tb_now_f *TB_Now;
extern tb_step_f *TB_Step;
extern tb_adjust_f *TB_Adjust;
void TS_Add(struct timestamp *ts, double dt);
struct timestamp *TS_Nanosec(struct timestamp *storage,
int64_t sec, int64_t nsec);
struct timestamp *TS_Double(struct timestamp *storage, double);
double TS_Diff(const struct timestamp *t1, const struct timestamp *t2);
void TS_SleepUntil(const struct timestamp *);
void TS_Format(char *buf, ssize_t len, const struct timestamp *ts);
void TS_RunTest(struct ocx *ocx);
/* todo.c -- todo-list scheduler **************************************/
struct todo; // private
enum todo_e {
TODO_FAIL = -1, // Break out of TODO_Run()
TODO_OK = 0,
TODO_DONE = 1, // Stop repeating me
};
typedef enum todo_e todo_f(struct ocx *, struct todolist *, void *priv);
struct todolist *TODO_NewList(void);
struct todo *TODO_ScheduleRel(struct todolist *, todo_f *func, void *priv,
double when, double repeat, const char *fmt, ...);
struct todo *TODO_ScheduleAbs(struct todolist *, todo_f *func, void *priv,
const struct timestamp *when, double repeat, const char *fmt, ...);
enum todo_e TODO_Run(struct ocx *ocx, struct todolist *);
/* combine_delta.c -- Source Combiner based on delta-pdfs *************/
struct combiner;
typedef void combine_f(struct ocx *, const struct combiner *,
double trust, double low, double mid, double high);
struct combiner {
unsigned magic;
#define COMBINER_MAGIC 0xab2b239c
combine_f *func;
void *priv;
const char *name1;
const char *name2;
};
struct combine_delta *CD_New(void);
struct combiner *CD_AddSource(struct combine_delta *,
const char *name1, const char *name2);
/**********************************************************************
* Main functions
*/
int main_client(int argc, char *const *argv);
int main_poll_server(int argc, char *const *argv);
int main_sim_client(int argc, char *const *argv);

71
ntimed_endian.h Normal file
View File

@ -0,0 +1,71 @@
/*-
* Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Part of:
* $FreeBSD: head/sys/sys/endian.h 208331 2010-05-20 06:16:13Z phk $
*/
#ifdef NTIMED_ENDIAN
#error "ntimed_endian.h included multiple times"
#endif
#define NTIMED_ENDIAN
/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
static __inline uint16_t
be16dec(const void *pp)
{
uint8_t const *p = (uint8_t const *)pp;
return ((p[0] << 8) | p[1]);
}
static __inline uint32_t
be32dec(const void *pp)
{
uint8_t const *p = (uint8_t const *)pp;
return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
}
static __inline void
be16enc(void *pp, uint16_t u)
{
uint8_t *p = (uint8_t *)pp;
p[0] = (u >> 8) & 0xff;
p[1] = u & 0xff;
}
static __inline void
be32enc(void *pp, uint32_t u)
{
uint8_t *p = (uint8_t *)pp;
p[0] = (u >> 24) & 0xff;
p[1] = (u >> 16) & 0xff;
p[2] = (u >> 8) & 0xff;
p[3] = u & 0xff;
}

186
ntimed_queue.h Normal file
View File

@ -0,0 +1,186 @@
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Part of:
* $FreeBSD: head/sys/sys/queue.h 251887 2013-06-18 02:57:56Z lstewart $
*/
#ifdef NTIMED_QUEUE_H
#error "ntimed_queue.h included multiple times"
#endif
#define NTIMED_QUEUE_H
/*
* Tail queue declarations.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* Tail queue functions.
*/
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
} \
} while (0)
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_FOREACH(var, head, field) \
for ((var) = TAILQ_FIRST((head)); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = TAILQ_LAST((head), headname); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST((head), headname); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_INIT(head) do { \
TAILQ_FIRST((head)) = NULL; \
(head)->tqh_last = &TAILQ_FIRST((head)); \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
TAILQ_NEXT((elm), field)->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else { \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
} \
TAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
TAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
TAILQ_FIRST((head))->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
TAILQ_FIRST((head)) = (elm); \
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
TAILQ_NEXT((elm), field) = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
} while (0)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_REMOVE(head, elm, field) do { \
if ((TAILQ_NEXT((elm), field)) != NULL) \
TAILQ_NEXT((elm), field)->field.tqe_prev = \
(elm)->field.tqe_prev; \
else { \
(head)->tqh_last = (elm)->field.tqe_prev; \
} \
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
} while (0)
#define TAILQ_SWAP(head1, head2, type, field) do { \
struct type *swap_first = (head1)->tqh_first; \
struct type **swap_last = (head1)->tqh_last; \
(head1)->tqh_first = (head2)->tqh_first; \
(head1)->tqh_last = (head2)->tqh_last; \
(head2)->tqh_first = swap_first; \
(head2)->tqh_last = swap_last; \
if ((swap_first = (head1)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head1)->tqh_first; \
else \
(head1)->tqh_last = &(head1)->tqh_first; \
if ((swap_first = (head2)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head2)->tqh_first; \
else \
(head2)->tqh_last = &(head2)->tqh_first; \
} while (0)

162
ntimed_tricks.h Normal file
View File

@ -0,0 +1,162 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef NTIMED_TRICKS_H
#error "ntimed_tricks.h included multiple times"
#endif
#define NTIMED_TRICKS_H
#include <assert.h>
/**********************************************************************
* Assert and friends
*
* We always runs with asserts enabled, and we assert liberally.
*
* Tests which are too expensive for production use should be wrapped
* in "#ifdef DIAGNOSTICS"
*
*/
#undef NDEBUG // Asserts *always* enabled.
#define AZ(foo) do { assert((foo) == 0); } while (0)
#define AN(foo) do { assert((foo) != 0); } while (0)
#define WRONG(foo) \
do { \
/*lint -save -e506 */ \
assert(0 == (uintptr_t)foo); \
/*lint -restore */ \
} while (0)
/**********************************************************************
* Safe printfs into compile-time fixed-size buffers.
*/
#define bprintf(buf, fmt, ...) \
do { \
assert(snprintf(buf, sizeof buf, fmt, __VA_ARGS__) \
< (int)sizeof buf); \
} while (0)
#define vbprintf(buf, fmt, ap) \
do { \
assert(vsnprintf(buf, sizeof buf, fmt, ap) \
< (int)sizeof buf); \
} while (0)
/**********************************************************************
* FlexeLint shutuppery
*
* Flexelint is a commercial LINT program from gimpel.com, and a very
* good one at that. We need a few special-case markups to tell it
* our intentions.
*/
/*
* In OO-light situations, functions have to match their prototype
* even if that means not const'ing a const'able argument.
* The typedef should be specified as argument to the macro.
*
*/
#define __match_proto__(xxx) /*lint -e{818} */
/**********************************************************************
* Mini object type checking
*
* This is a trivial struct type checking framework I have used with
* a lot of success in a number of high-rel software projects over the
* years. It is particularly valuable when you pass things trough void
* pointers or opaque APIs.
*
* Define your struct like this:
* struct foobar {
* unsigned magic;
* #define FOOBAR_MAGIC 0x23923092
* ...
* }
*
* The "magic" element SHALL be the first element of the struct.
*
* The MAGIC number you get from "od -x < /dev/random | head -1" or
* similar. It is important that each structure has its own distinct
* value.
*
*/
#define INIT_OBJ(to, type_magic) \
do { \
(void)memset(to, 0, sizeof *to); \
(to)->magic = (type_magic); \
} while (0)
#define ALLOC_OBJ(to, type_magic) \
do { \
(to) = calloc(1L, sizeof *(to)); \
if ((to) != NULL) \
(to)->magic = (type_magic); \
} while (0)
#define FREE_OBJ(to) \
do { \
(to)->magic = (0); \
free(to); \
} while (0)
#define VALID_OBJ(ptr, type_magic) \
((ptr) != NULL && (ptr)->magic == (type_magic))
#define CHECK_OBJ(ptr, type_magic) \
do { \
assert((ptr)->magic == type_magic); \
} while (0)
#define CHECK_OBJ_NOTNULL(ptr, type_magic) \
do { \
assert((ptr) != NULL); \
assert((ptr)->magic == type_magic); \
} while (0)
#define CHECK_OBJ_ORNULL(ptr, type_magic) \
do { \
if ((ptr) != NULL) \
assert((ptr)->magic == type_magic); \
} while (0)
#define CAST_OBJ(to, from, type_magic) \
do { \
(to) = (from); \
if ((to) != NULL) \
CHECK_OBJ((to), (type_magic)); \
} while (0)
#define CAST_OBJ_NOTNULL(to, from, type_magic) \
do { \
(to) = (from); \
assert((to) != NULL); \
CHECK_OBJ((to), (type_magic)); \
} while (0)

137
ntp.h Normal file
View File

@ -0,0 +1,137 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* NTP protocol stuff
* ==================
*
*/
struct sockaddr;
struct ntp_peer;
#ifdef NTP_H_INCLUDED
#error "ntp.h included multiple times"
#endif
#define NTP_H_INCLUDED
enum ntp_mode {
#define NTP_MODE(n, l, u) NTP_MODE_##u = n,
#include "ntp_tbl.h"
#undef NTP_MODE
};
enum ntp_leap {
#define NTP_LEAP(n, l, u) NTP_LEAP_##u = n,
#include "ntp_tbl.h"
#undef NTP_LEAP
};
/* ntp_packet.c -- [De]Serialisation **********************************/
struct ntp_packet {
unsigned magic;
#define NTP_PACKET_MAGIC 0x78b7f0be
enum ntp_leap ntp_leap;
uint8_t ntp_version;
enum ntp_mode ntp_mode;
uint8_t ntp_stratum;
uint8_t ntp_poll;
int8_t ntp_precision;
struct timestamp ntp_delay;
struct timestamp ntp_dispersion;
uint8_t ntp_refid[4];
struct timestamp ntp_reference;
struct timestamp ntp_origin;
struct timestamp ntp_receive;
struct timestamp ntp_transmit;
struct timestamp ts_rx;
};
struct ntp_packet *NTP_Packet_Unpack(struct ntp_packet *dst, void *ptr,
ssize_t len);
ssize_t NTP_Packet_Pack(void *ptr, ssize_t len, struct ntp_packet *);
/* ntp_tools.c -- Handy tools *****************************************/
void NTP_Tool_Client_Req(struct ntp_packet *);
void NTP_Tool_Format(char *p, ssize_t len, const struct ntp_packet *pkt);
int NTP_Tool_Scan(struct ntp_packet *pkt, const char *buf);
/* ntp_filter.c -- NTP sanity checking ********************************/
typedef void ntp_filter_f(struct ocx *, const struct ntp_peer *);
void NF_New(struct ntp_peer *);
void NF_Init(void);
/* ntp_peer.c -- State management *************************************/
struct ntp_group;
struct ntp_peer {
unsigned magic;
#define NTP_PEER_MAGIC 0xbf0740a0
char *hostname;
char *ip;
struct sockaddr *sa;
unsigned sa_len;
struct ntp_packet *tx_pkt;
struct ntp_packet *rx_pkt;
ntp_filter_f *filter_func;
void *filter_priv;
struct combiner *combiner;
// For ntp_peerset.c
TAILQ_ENTRY(ntp_peer) list;
struct ntp_group *group;
};
struct ntp_peer *NTP_Peer_New(const char *name, const void *, unsigned);
struct ntp_peer *NTP_Peer_NewLookup(struct ocx *ocx, const char *name);
void NTP_Peer_Destroy(struct ntp_peer *np);
int NTP_Peer_Poll(struct ocx *, int fd, const struct ntp_peer *, double tmo);
/* ntp_peerset.c -- Peer set management ****************************/
struct ntp_peerset *NTP_PeerSet_New(struct ocx *);
void NTP_PeerSet_AddPeer(struct ocx *ocx, struct ntp_peerset *npl,
struct ntp_peer *np);
int NTP_PeerSet_Add(struct ocx *, struct ntp_peerset *,
const char *hostname);
void NTP_PeerSet_Poll(struct ocx *, struct ntp_peerset *, int,
struct todolist *);
struct ntp_peer *NTP_PeerSet_Iter0(const struct ntp_peerset *);
struct ntp_peer *NTP_PeerSet_IterN(const struct ntp_peerset *,
const struct ntp_peer *);
#define NTP_PeerSet_Foreach(var, npl) \
for(var = NTP_PeerSet_Iter0(npl); \
var != NULL; \
var = NTP_PeerSet_IterN(npl, var))

192
ntp_filter.c Normal file
View File

@ -0,0 +1,192 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Filter incoming NTP packets
* ===========================
*/
#include <math.h>
#include <stdlib.h>
#include "ntimed.h"
#include "ntp.h"
#define PARAM_NTP_FILTER PARAM_INSTANCE
#define PARAM_TABLE_NAME ntp_filter_param_table
#include "param_instance.h"
#undef PARAM_TABLE_NAME
#undef PARAM_NTP_FILTER
struct ntp_filter {
unsigned magic;
#define NTP_FILTER_MAGIC 0xf7b7032d
double lo, mid, hi;
double alo, amid, ahi;
double alolo, ahihi;
double navg;
double trust;
};
static void __match_proto__(ntp_filter_f)
nf_filter(struct ocx *ocx, const struct ntp_peer *np)
{
struct ntp_filter *nf;
struct ntp_packet *rxp;
int branch, fail_hi, fail_lo;
double lo_noise, hi_noise;
double lo_lim, hi_lim;
double r;
char buf[256];
CAST_OBJ_NOTNULL(nf, np->filter_priv, NTP_FILTER_MAGIC);
rxp = np->rx_pkt;
CHECK_OBJ_NOTNULL(rxp, NTP_PACKET_MAGIC);
NTP_Tool_Format(buf, sizeof buf, rxp);
Put(NULL, OCX_TRACE, "NTP_Packet %+f %s %s %s\n",
np->hostname, np->ip, buf);
if (rxp->ntp_leap == NTP_LEAP_UNKNOWN)
return; // XXX diags
// XXX: Check leap warnings in wrong months
// XXX: Check leap warnings against other sources
if (rxp->ntp_version < 3 || rxp->ntp_version > 4) {
Put(ocx, OCX_TRACE, "NF Bad version %d\n", rxp->ntp_version);
return;
}
if (rxp->ntp_mode != NTP_MODE_SERVER) {
Put(ocx, OCX_TRACE, "NF Bad mode %d\n", rxp->ntp_mode);
return;
}
if (rxp->ntp_stratum == 0 || rxp->ntp_stratum > 15) {
Put(ocx, OCX_TRACE, "NF Bad stratum %d\n", rxp->ntp_stratum);
return;
}
r = TS_Diff(&rxp->ntp_transmit, &rxp->ntp_receive);
if (r <= 0.0) {
Put(ocx, OCX_TRACE, "NF rx after tx %.3e\n", r);
return;
}
r = TS_Diff(&rxp->ntp_transmit, &rxp->ntp_reference);
if (r < -2e-9) {
/* two nanoseconds to Finagle rounding errors */
Put(ocx, OCX_TRACE, "NF ref after tx %.3e\n", r);
return; // XXX diags
}
// This is almost never a good sign.
if (r > 2048) {
/* XXX: 2048 -> param */
Put(ocx, OCX_TRACE, "NF ancient ref %.3e\n", r);
return;
}
if (nf->navg < param_ntp_filter_average)
nf->navg += 1;
nf->lo = TS_Diff(&rxp->ntp_origin, &rxp->ntp_receive);
nf->hi = TS_Diff(&rxp->ts_rx, &rxp->ntp_transmit);
nf->mid = .5 * (nf->lo + nf->hi);
if (nf->navg > 2) {
lo_noise = sqrt(nf->alolo - nf->alo * nf->alo);
hi_noise = sqrt(nf->ahihi - nf->ahi * nf->ahi);
} else {
lo_noise = 0.0;
hi_noise = 0.0;
}
lo_lim = nf->alo - lo_noise * param_ntp_filter_threshold;
hi_lim = nf->ahi + hi_noise * param_ntp_filter_threshold;
fail_lo = nf->lo < lo_lim;
fail_hi = nf->hi > hi_lim;
if (fail_lo && fail_hi) {
branch = 1;
} else if (nf->navg > 3 && fail_lo) {
nf->mid = nf->amid + (nf->hi - nf->ahi);
branch = 2;
} else if (nf->navg > 3 && fail_hi) {
nf->mid = nf->amid + nf->lo - nf->alo;
branch = 3;
} else {
branch = 4;
}
r = nf->navg;
if (nf->navg > 2 && branch != 4)
r *= r;
nf->alo += (nf->lo - nf->alo) / r;
nf->amid += (nf->mid - nf->amid) / r;
nf->ahi += (nf->hi - nf->ahi) / r;
nf->alolo += (nf->lo*nf->lo - nf->alolo) / r;
nf->ahihi += (nf->hi*nf->hi - nf->ahihi) / r;
if (rxp->ntp_stratum == 0)
nf->trust = 0.0;
else if (rxp->ntp_stratum == 15)
nf->trust = 0.0;
else
nf->trust = 1.0 / rxp->ntp_stratum;
Put(ocx, OCX_TRACE,
"NTP_Filter %s %s %d %.3e %.3e %.3e %.3e %.3e %.3e\n",
np->hostname, np->ip, branch,
nf->lo, nf->mid, nf->hi,
lo_lim, nf->amid, hi_lim);
if (np->combiner->func != NULL)
np->combiner->func(ocx, np->combiner,
nf->trust, nf->lo, nf->mid, nf->hi);
}
void
NF_New(struct ntp_peer *np)
{
struct ntp_filter *nf;
ALLOC_OBJ(nf, NTP_FILTER_MAGIC);
AN(nf);
np->filter_func = nf_filter;
np->filter_priv = nf;
}
void
NF_Init(void)
{
Param_Register(ntp_filter_param_table);
}

183
ntp_packet.c Normal file
View File

@ -0,0 +1,183 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* NTP packet (de)serialization
* ============================
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0 |LI | VN |Mode | Stratum | Poll | Precision |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 4 | Root Delay |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 8 | Root Dispersion |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 12 | Reference ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 16 | |
* + Reference Timestamp (64) +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 24 | |
* + Origin Timestamp (64) +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 32 | |
* + Receive Timestamp (64) +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 40 | |
* + Transmit Timestamp (64) +
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
#include <stdlib.h>
#include <string.h>
#include "ntimed.h"
#include "ntp.h"
#include "ntimed_endian.h"
/*
* Seconds between 1900 (NTP epoch) and 1970 (UNIX epoch).
* 17 is the number of leapdays.
*/
#define NTP_UNIX (((1970U - 1900U) * 365U + 17U) * 24U * 60U * 60U)
/**********************************************************************
* Picking a NTP packet apart in a safe, byte-order agnostic manner
*/
static void
ntp64_2ts(struct timestamp *ts, const uint8_t *ptr)
{
INIT_OBJ(ts, TIMESTAMP_MAGIC);
ts->sec = be32dec(ptr) - NTP_UNIX;
ts->frac = (uint64_t)be32dec(ptr + 4) << 32ULL;
}
static void
ntp32_2ts(struct timestamp *ts, const uint8_t *ptr)
{
INIT_OBJ(ts, TIMESTAMP_MAGIC);
ts->sec = be16dec(ptr);
ts->frac = (uint64_t)be16dec(ptr + 2) << 48ULL;
}
struct ntp_packet *
NTP_Packet_Unpack(struct ntp_packet *np, void *ptr, ssize_t len)
{
uint8_t *p = ptr;
AN(ptr);
if (len != 48) {
/* XXX: Diagnostic */
return (NULL);
}
if (np == NULL) {
ALLOC_OBJ(np, NTP_PACKET_MAGIC);
AN(np);
} else {
INIT_OBJ(np, NTP_PACKET_MAGIC);
}
np->ntp_leap = (enum ntp_leap)(p[0] >> 6);
np->ntp_version = (p[0] >> 3) & 0x7;
np->ntp_mode = (enum ntp_mode)(p[0] & 0x07);
np->ntp_stratum = p[1];
np->ntp_poll = p[2];
np->ntp_precision = (int8_t)p[3];
ntp32_2ts(&np->ntp_delay, p + 4);
ntp32_2ts(&np->ntp_dispersion, p + 8);
memcpy(np->ntp_refid, p + 12, 4L);
ntp64_2ts(&np->ntp_reference, p + 16);
ntp64_2ts(&np->ntp_origin, p + 24);
ntp64_2ts(&np->ntp_receive, p + 32);
ntp64_2ts(&np->ntp_transmit, p + 40);
return (np);
}
/**********************************************************************
* Putting a NTP packet apart in a safe, byte-order agnostic manner
*/
static void
ts_2ntp32(uint8_t *dst, const struct timestamp *ts)
{
CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC);
assert(ts->sec < 65536);
be16enc(dst, ts->sec);
be16enc(dst + 2, ts->frac >> 48ULL);
}
static void
ts_2ntp64(uint8_t *dst, const struct timestamp *ts)
{
CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC);
be32enc(dst, ts->sec + NTP_UNIX);
be32enc(dst + 4, ts->frac >> 32ULL);
}
ssize_t
NTP_Packet_Pack(void *ptr, ssize_t len, struct ntp_packet *np)
{
uint8_t *pbuf = ptr;
AN(ptr);
assert(len >= 48);
CHECK_OBJ_NOTNULL(np, NTP_PACKET_MAGIC);
assert(np->ntp_version < 8);
assert(np->ntp_stratum < 15);
pbuf[0] = (uint8_t)np->ntp_leap << 6;
pbuf[0] |= np->ntp_version << 3;
pbuf[0] |= (uint8_t)np->ntp_mode;
pbuf[1] = np->ntp_stratum;
pbuf[2] = np->ntp_poll;
pbuf[3] = (uint8_t)np->ntp_precision;
ts_2ntp32(pbuf + 4, &np->ntp_delay);
ts_2ntp32(pbuf + 8, &np->ntp_dispersion);
memcpy(pbuf + 12, np->ntp_refid, 4L);
ts_2ntp64(pbuf + 16, &np->ntp_reference);
ts_2ntp64(pbuf + 24, &np->ntp_origin);
ts_2ntp64(pbuf + 32, &np->ntp_receive);
TB_Now(&np->ntp_transmit);
ts_2ntp64(pbuf + 40, &np->ntp_transmit);
/* Reverse again, to avoid subsequent trouble from rounding. */
ntp64_2ts(&np->ntp_transmit, pbuf + 40);
return (48);
}

178
ntp_peer.c Normal file
View File

@ -0,0 +1,178 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <netdb.h>
#include <sys/socket.h>
#include "ntimed.h"
#include "udp.h"
#include "ntp.h"
struct ntp_peer *
NTP_Peer_New(const char *hostname, const void *sa, unsigned salen)
{
struct ntp_peer *np;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
AZ(getnameinfo(sa, salen, hbuf, sizeof(hbuf), sbuf,
sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV));
ALLOC_OBJ(np, NTP_PEER_MAGIC);
AN(np);
np->sa_len = salen;
np->sa = calloc(1, np->sa_len);
AN(np->sa);
memcpy(np->sa, sa, np->sa_len);
np->hostname = strdup(hostname);
AN(np->hostname);
np->ip = strdup(hbuf);
AN(np->ip);
ALLOC_OBJ(np->tx_pkt, NTP_PACKET_MAGIC);
AN(np->tx_pkt);
ALLOC_OBJ(np->rx_pkt, NTP_PACKET_MAGIC);
AN(np->rx_pkt);
NTP_Tool_Client_Req(np->tx_pkt);
return (np);
}
struct ntp_peer *
NTP_Peer_NewLookup(struct ocx *ocx, const char *hostname)
{
struct addrinfo hints, *res0;
int error;
struct ntp_peer *np;
memset(&hints, 0, sizeof hints);
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
error = getaddrinfo(hostname, "ntp", &hints, &res0);
if (error)
Fail(ocx, 0, "hostname '%s', port 'ntp': %s\n",
hostname, gai_strerror(error));
np = NTP_Peer_New(hostname, res0->ai_addr, res0->ai_addrlen);
freeaddrinfo(res0);
return (np);
}
void
NTP_Peer_Destroy(struct ntp_peer *np)
{
CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC);
free(np->sa);
free(np->hostname);
free(np->ip);
free(np->tx_pkt);
free(np->rx_pkt);
FREE_OBJ(np);
}
int
NTP_Peer_Poll(struct ocx *ocx, int fd, const struct ntp_peer *np, double tmo)
{
char buf[100];
ssize_t len;
struct sockaddr_storage rss;
socklen_t rssl;
ssize_t l;
int i, timeout_msec;
struct pollfd pfd[1];
struct timestamp t0, t1, t2;
double d;
assert(fd >= 0);
CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC);
assert(tmo > 0.0 && tmo <= 1.0);
len = NTP_Packet_Pack(buf, sizeof buf, np->tx_pkt);
l = sendto(fd, buf, len, 0, np->sa, np->sa_len);
if (l != len)
Fail(ocx, l < 0 ? 1 : 0,
"Tx peer '%s' @%s got %d", np->hostname, np->ip, l);
(void)TB_Now(&t0);
while (1) {
(void)TB_Now(&t1);
d = TS_Diff(&t1, &t0);
timeout_msec = lround(1e3 * (tmo - d));
pfd[0].fd = fd;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
if (timeout_msec <= 0)
i = 0;
else
i = poll(pfd, 1, timeout_msec);
if (i < 0)
Fail(ocx, 1, "poll(2) failed\n");
if (i == 0)
return (0);
i = UdpTimedRx(ocx, fd, &rss, &rssl, &t2, buf, sizeof buf);
if (i < 0)
Fail(ocx, 1, "Rx failed\n");
if (i != 48) {
Debug(ocx, "Rx peer %s @%s got len=%d\n",
np->hostname, i);
continue;
}
/* Ignore packets from other hosts */
if (np->sa_len != rssl || memcmp(np->sa, &rss, rssl))
continue;
AN(NTP_Packet_Unpack(np->rx_pkt, buf, i));
np->rx_pkt->ts_rx = t2;
/* Ignore packets which are not replies to our packet */
if (TS_Diff(&np->tx_pkt->ntp_transmit,
&np->rx_pkt->ntp_origin) != 0.0) {
continue;
}
return (1);
}
}

237
ntp_peerset.c Normal file
View File

@ -0,0 +1,237 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include "ntimed.h"
#include "ntp.h"
struct ntp_group {
unsigned magic;
#define NTP_GROUP_MAGIC 0xdd5f58de
TAILQ_ENTRY(ntp_group) list;
char *hostname;
int npeer;
};
struct ntp_peerset {
unsigned magic;
#define NTP_PEERSET_MAGIC 0x0bf873d0
TAILQ_HEAD(,ntp_peer) head;
int npeer;
TAILQ_HEAD(,ntp_group) group;
int ngroup;
int fd;
double t0;
double init_duration;
double poll_period;
double init_packets;
};
/**********************************************************************/
struct ntp_peerset *
NTP_PeerSet_New(struct ocx *ocx)
{
struct ntp_peerset *npl;
(void)ocx;
ALLOC_OBJ(npl, NTP_PEERSET_MAGIC);
AN(npl);
TAILQ_INIT(&npl->head);
TAILQ_INIT(&npl->group);
return (npl);
}
/**********************************************************************
* Iterator helpers
*/
struct ntp_peer *
NTP_PeerSet_Iter0(const struct ntp_peerset *npl)
{
CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC);
return (TAILQ_FIRST(&npl->head));
}
struct ntp_peer *
NTP_PeerSet_IterN(const struct ntp_peerset *npl, const struct ntp_peer *np)
{
CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC);
CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC);
return (TAILQ_NEXT(np, list));
}
/**********************************************************************/
void
NTP_PeerSet_AddPeer(struct ocx *ocx, struct ntp_peerset *npl,
struct ntp_peer *np)
{
(void)ocx;
CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC);
CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC);
TAILQ_INSERT_TAIL(&npl->head, np, list);
npl->npeer++;
}
/**********************************************************************/
int
NTP_PeerSet_Add(struct ocx *ocx, struct ntp_peerset *npl,
const char *hostname)
{
struct addrinfo hints, *res, *res0;
int error, n;
struct ntp_peer *np;
struct ntp_group *ng;
CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC);
memset(&hints, 0, sizeof hints);
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
error = getaddrinfo(hostname, "ntp", &hints, &res0);
if (error)
Fail(ocx, 1, "hostname '%s', port 'ntp': %s\n",
hostname, gai_strerror(error));
ALLOC_OBJ(ng, NTP_GROUP_MAGIC);
AN(ng);
n = 0;
for (res = res0; res; res = res->ai_next) {
np = NTP_Peer_New(hostname, res->ai_addr, res->ai_addrlen);
// XXX: duplicate check
TAILQ_INSERT_TAIL(&npl->head, np, list);
npl->npeer++;
np->group = ng;
ng->npeer++;
n++;
}
freeaddrinfo(res0);
if (ng->npeer == 0) {
FREE_OBJ(ng);
} else {
ng->hostname = strdup(hostname);
AN(ng->hostname);
TAILQ_INSERT_TAIL(&npl->group, ng, list);
npl->ngroup++;
}
return (n);
}
/**********************************************************************
* This function is responsible for polling the peers in the set.
*/
static enum todo_e
ntp_peerset_poll(struct ocx *ocx, struct todolist *tdl, void *priv)
{
struct ntp_peerset *npl;
struct ntp_peer *np;
double d, dt;
(void)ocx;
CAST_OBJ_NOTNULL(npl, priv, NTP_PEERSET_MAGIC);
AN(tdl);
np = TAILQ_FIRST(&npl->head);
if (np == NULL)
return(TODO_DONE);
CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC);
TAILQ_REMOVE(&npl->head, np, list);
TAILQ_INSERT_TAIL(&npl->head, np, list);
d = npl->poll_period / npl->npeer;
if (npl->t0 < npl->init_duration) {
dt = exp(
log(npl->init_duration) / (npl->init_packets * npl->npeer));
if (npl->t0 * dt < npl->init_duration)
d = npl->t0 * dt - npl->t0;
}
npl->t0 += d;
TODO_ScheduleRel(tdl, ntp_peerset_poll, npl, d, 0.0, "NTP_PeerSet");
if (NTP_Peer_Poll(ocx, npl->fd, np, 0.8)) {
if (np->filter_func != NULL)
np->filter_func(ocx, np);
}
return (TODO_OK);
}
/**********************************************************************
* This function will be responsible for maintaining the peers in the set
*
* XXX: Pick only the best N (3?) servers from any hostname as active.
* XXX: Re-lookup hostnames on periodic basis to keep the set current
* XXX: Implement (a future) pool.ntp.org load-balancing protocol
*/
static enum todo_e
ntp_peerset_herd(struct ocx *ocx, struct todolist *tdl, void *priv)
{
(void)ocx;
(void)tdl;
(void)priv;
return (TODO_DONE);
}
/**********************************************************************/
void
NTP_PeerSet_Poll(struct ocx *ocx, struct ntp_peerset *npl, int fd,
struct todolist *tdl)
{
(void)ocx;
CHECK_OBJ_NOTNULL(npl, NTP_PEERSET_MAGIC);
assert(fd >= 0);
AN(tdl);
npl->fd = fd;
npl->t0 = 1.0;
npl->init_duration = 64.;
npl->init_packets = 6.;
npl->poll_period = 64.;
TODO_ScheduleRel(tdl, ntp_peerset_poll, npl, 0.0, 0.0,
"NTP_PeerSet Poll");
if (npl->ngroup > 0)
TODO_ScheduleRel(tdl, ntp_peerset_herd, npl,
15. * 60. / npl->ngroup, 0.0, "NTP_PeerSet Herd");
}

49
ntp_tbl.h Normal file
View File

@ -0,0 +1,49 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Table generator file for tables related to the NTP protocol.
*/
/*lint -save -e525 -e539 */
#ifdef NTP_MODE
NTP_MODE(0, mode0, MODE0)
NTP_MODE(1, symact, SYMACT)
NTP_MODE(2, sympas, SYMPAS)
NTP_MODE(3, client, CLIENT)
NTP_MODE(4, server, SERVER)
NTP_MODE(5, bcast, BCAST)
NTP_MODE(6, ctrl, CTRL)
NTP_MODE(7, mode7, MODE7)
#endif
/*lint -restore */
/*lint -save -e525 -e539 */
#ifdef NTP_LEAP
NTP_LEAP(0, none, NONE)
NTP_LEAP(1, ins, INS)
NTP_LEAP(2, del, DEL)
NTP_LEAP(3, unknown, UNKNOWN)
#endif
/*lint -restore */

201
ntp_tools.c Normal file
View File

@ -0,0 +1,201 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* NTP tools
* =========
*
*
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "ntimed.h"
#include "ntp.h"
#include "ntimed_endian.h"
/**********************************************************************
* Build a standard client query packet
*/
void
NTP_Tool_Client_Req(struct ntp_packet *np)
{
AN(np);
INIT_OBJ(np, NTP_PACKET_MAGIC);
np->ntp_leap = NTP_LEAP_UNKNOWN;
np->ntp_version = 4;
np->ntp_mode = NTP_MODE_CLIENT;
np->ntp_stratum = 0;
np->ntp_poll = 4;
np->ntp_precision = -6;
INIT_OBJ(&np->ntp_delay, TIMESTAMP_MAGIC);
np->ntp_delay.sec = 1;
INIT_OBJ(&np->ntp_dispersion, TIMESTAMP_MAGIC);
np->ntp_dispersion.sec = 1;
INIT_OBJ(&np->ntp_reference, TIMESTAMP_MAGIC);
INIT_OBJ(&np->ntp_origin, TIMESTAMP_MAGIC);
INIT_OBJ(&np->ntp_receive, TIMESTAMP_MAGIC);
}
/**********************************************************************
* Format a NTP packet in a standardized layout for subsequent parsing.
*
* We dump absolute timestamps relative to the origin timestamp.
*
* XXX: Nanosecond precision is enough for everybody.
*/
void
NTP_Tool_Format(char *p, ssize_t len, const struct ntp_packet *pkt)
{
char *e;
char buf[40];
AN(p);
assert(len > 0);
CHECK_OBJ_NOTNULL(pkt, NTP_PACKET_MAGIC);
e = p + len;
p += snprintf(p, e - p, "[%u", pkt->ntp_leap);
assert(p < e);
p += snprintf(p, e - p, " %u", pkt->ntp_version);
assert(p < e);
p += snprintf(p, e - p, " %u", pkt->ntp_mode);
assert(p < e);
p += snprintf(p, e - p, " %3u", pkt->ntp_stratum);
assert(p < e);
p += snprintf(p, e - p, " %3u", pkt->ntp_poll);
assert(p < e);
p += snprintf(p, e - p, " %4d", pkt->ntp_precision);
assert(p < e);
TS_Format(buf, sizeof buf, &pkt->ntp_delay);
p += snprintf(p, e - p, " %s", buf); assert(p < e);
assert(p < e);
TS_Format(buf, sizeof buf, &pkt->ntp_dispersion);
p += snprintf(p, e - p, " %s", buf); assert(p < e);
assert(p < e);
p += snprintf(p, e - p, " 0x%02x%02x%02x%02x",
pkt->ntp_refid[0], pkt->ntp_refid[1],
pkt->ntp_refid[2], pkt->ntp_refid[3]);
assert(p < e);
p += snprintf(p, e - p, " %.9f",
TS_Diff(&pkt->ntp_reference, &pkt->ntp_origin));
assert(p < e);
TS_Format(buf, sizeof buf, &pkt->ntp_origin);
p += snprintf(p, e - p, " %s", buf); assert(p < e);
assert(p < e);
p += snprintf(p, e - p, " %.9f",
TS_Diff(&pkt->ntp_receive, &pkt->ntp_origin));
assert(p < e);
p += snprintf(p, e - p, " %.9f",
TS_Diff(&pkt->ntp_transmit, &pkt->ntp_receive));
assert(p < e);
if (pkt->ts_rx.sec && pkt->ts_rx.frac) {
p += snprintf(p, e - p, " %.9f]",
TS_Diff(&pkt->ts_rx, &pkt->ntp_transmit));
} else {
p += snprintf(p, e - p, " %.9f]", 0.0);
}
assert(p < e);
}
/**********************************************************************
* Scan a packet in NTP_Tool_Format layout.
*/
int
NTP_Tool_Scan(struct ntp_packet *pkt, const char *buf)
{
unsigned u_fields[8];
double d_fields[7];
char cc;
int i;
i = sscanf(buf,
"[%u %u %u %u %u %lf %lf %lf %x %lf %u.%u %lf %lf %lf%c",
u_fields + 0, /* NTP_leap */
u_fields + 1, /* NTP_version */
u_fields + 2, /* NTP_mode */
u_fields + 3, /* NTP_stratum */
u_fields + 4, /* NTP_poll */
d_fields + 0, /* NTP_precision */
d_fields + 1, /* NTP_delay */
d_fields + 2, /* NTP_dispersion */
u_fields + 5, /* NTP_refid */
d_fields + 3, /* NTP_reference - NTP_origin */
u_fields + 6, /* NTP_origin:sec */
u_fields + 7, /* NTP_origin:nsec */
d_fields + 4, /* NTP_receive - NTP_origin */
d_fields + 5, /* NTP_transmit - NTP_receive */
d_fields + 6, /* ts_rx - NTP_transmit*/
&cc);
if (i != 16 || cc != ']')
return (-1);
INIT_OBJ(pkt, NTP_PACKET_MAGIC);
pkt->ntp_leap = (enum ntp_leap)u_fields[0];
pkt->ntp_version = u_fields[1];
pkt->ntp_mode = (enum ntp_mode)u_fields[2];
pkt->ntp_stratum = u_fields[3];
pkt->ntp_poll = u_fields[4];
pkt->ntp_precision = (int8_t)floor(d_fields[0]);
TS_Double(&pkt->ntp_delay, d_fields[1]);
TS_Double(&pkt->ntp_dispersion, d_fields[2]);
be32enc(pkt->ntp_refid, u_fields[5]);
TS_Nanosec(&pkt->ntp_origin, u_fields[6], u_fields[7]);
pkt->ntp_reference = pkt->ntp_origin;
TS_Add(&pkt->ntp_reference, d_fields[3]);
pkt->ntp_receive = pkt->ntp_origin;
TS_Add(&pkt->ntp_receive, d_fields[4]);
pkt->ntp_transmit = pkt->ntp_receive;
TS_Add(&pkt->ntp_transmit, d_fields[5]);
if (d_fields[6] != 0.0) {
pkt->ts_rx = pkt->ntp_transmit;
TS_Add(&pkt->ts_rx, d_fields[6]);
}
return (0);
}

172
ocx_stdio.c Normal file
View File

@ -0,0 +1,172 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Operational Context STDIO
* =========================
*
* "The most effective debugging tool is still careful thought,
* coupled with judiciously placed print statements."
* -- Brian Kernighan, "Unix for Beginners" (1979)
*
* The problem with print statements is where they end up. For instance
* in a server with a CLI interface, you want the print statements to go
* to the CLI session which called the code.
*
* An "Operational Context" is a back-pointer to where the print statement
* should end up.
*
* We operate with three "channels", DIAG, TRACE and DEBUG.
*
* DIAG is mandatory output, error messages, diagnostics etc.
* This should always end up where the action was initiated.
*
* DEBUG is optional output which may be supressed.
* This should go where DIAG goes, unless specifically redirected
* by the operator.
*
* TRACE is data collection, statistics etc.
* In general this goes nowhere unless configured to end up
* somewhere.
*
* About this implementation:
*
* This is a very naiive implementation spitting things out to stdout/stderr,
* knowing that we are a single threaded program.
*
* XXX: Pull in sbufs to do it right.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "ntimed.h"
static FILE *tracefile;
static FILE *
getdst(enum ocx_chan chan)
{
if (chan == OCX_DIAG)
return (stderr);
if (chan == OCX_TRACE)
return (tracefile);
if (chan == OCX_DEBUG)
return (stdout);
WRONG("Wrong ocx_chan");
return (NULL);
}
static void __match_proto__()
putv(struct ocx *ocx, enum ocx_chan chan, const char *fmt, va_list ap)
{
FILE *dst = getdst(chan);
va_list ap2;
va_copy(ap2, ap);
AZ(ocx);
if (dst != NULL)
(void)vfprintf(dst, fmt, ap);
if (chan == OCX_DIAG)
vsyslog(LOG_ERR, fmt, ap2);
}
/**********************************************************************
* XXX: take strftime format string to chop tracefiles in time.
*/
void
ArgTracefile(const char *fn)
{
if (tracefile != NULL && tracefile != stdout) {
AZ(fclose(tracefile));
tracefile = NULL;
}
if (fn == NULL)
return;
if (!strcmp(fn, "-")) {
tracefile = stdout;
return;
}
tracefile = fopen(fn, "w");
if (tracefile == NULL)
Fail(NULL, 1, "Could not open '%s' for writing", fn);
setbuf(tracefile, NULL);
}
/**********************************************************************
* XXX: The stuff below is generic and really ought to be in ocx.c on
* XXX: its own.
*/
void
Put(struct ocx *ocx, enum ocx_chan chan, const char *fmt, ...)
{
va_list ap;
AZ(ocx);
va_start(ap, fmt);
putv(ocx, chan, fmt, ap);
va_end(ap);
}
void
PutHex(struct ocx *ocx, enum ocx_chan chan, const void *ptr, ssize_t len)
{
const uint8_t *p = ptr;
const char *s = "";
AN(ptr);
assert(len >= 0);
while(len--) {
Put(ocx, chan, "%s%02x", s, *p++);
s = " ";
}
}
void
Fail(struct ocx *ocx, int err, const char *fmt, ...)
{
va_list ap;
if (err)
err = errno;
Put(ocx, OCX_DIAG, "Failure: ");
va_start(ap, fmt);
putv(ocx, OCX_DIAG, fmt, ap);
va_end(ap);
Put(ocx, OCX_DIAG, "\n");
if (err)
Put(ocx, OCX_DIAG, "errno = %d (%s)\n", err, strerror(err));
exit(1);
}

160
param.c Normal file
View File

@ -0,0 +1,160 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Routines to deal with parameters
*
*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "ntimed.h"
static TAILQ_HEAD(, param_tbl) param_tbl = TAILQ_HEAD_INITIALIZER(param_tbl);
void
Param_Register(struct param_tbl *pt)
{
for (;pt->name != NULL; pt++)
TAILQ_INSERT_TAIL(&param_tbl, pt, list);
}
static void
param_wrapline(struct ocx *ocx, const char *b)
{
int n = 0;
const char *e, *w;
const int tabs = 8;
const int wrap_at = 64;
AN(b);
Put(ocx, OCX_DIAG, "\t");
e = strchr(b, '\0');
while (b < e) {
if (!isspace(*b)) {
Put(ocx, OCX_DIAG, "%c", *b);
b++;
n++;
} else if (*b == '\t') {
do {
Put(ocx, OCX_DIAG, " ");
n++;
} while ((n % tabs) != 1);
b++;
} else if (*b == '\n') {
Put(ocx, OCX_DIAG, "\n");
param_wrapline(ocx, b + 1);
return;
} else {
assert (*b == ' ');
for (w = b + 1; w < e; w++)
if (isspace(*w))
break;
if (n + (w - b) < wrap_at) {
Put(ocx, OCX_DIAG, "%.*s", (int)(w - b), b);
n += (w - b);
b = w;
} else {
Put(ocx, OCX_DIAG, "\n");
param_wrapline(ocx, b + 1);
return;
}
}
}
}
void
Param_Tweak(struct ocx *ocx, const char *arg)
{
struct param_tbl *pt;
const char *q;
char *r;
double d;
size_t l;
if (!strcmp(arg, "?")) {
Put(ocx, OCX_DIAG, "List of available parameters:\n");
TAILQ_FOREACH(pt, &param_tbl, list)
Put(ocx, OCX_DIAG, "\t%s\n", pt->name);
Fail(ocx, 0, "Stopping after parameter query.\n");
}
q = strchr(arg, '=');
if (q == NULL) {
TAILQ_FOREACH(pt, &param_tbl, list)
if (!strcmp(pt->name, arg))
break;
if (pt == NULL)
Fail(ocx, 0, "-p unknown parameter '%s' (try -p '?')",
arg);
Put(ocx, OCX_DIAG, "Parameter:\n\t%s\n", pt->name);
Put(ocx, OCX_DIAG, "Minimum:\n\t%.3e\n", pt->min);
Put(ocx, OCX_DIAG, "Maximum:\n\t%.3e\n", pt->max);
Put(ocx, OCX_DIAG, "Default:\n\t%.3e\n", pt->def);
Put(ocx, OCX_DIAG, "Description:\n");
param_wrapline(ocx, pt->doc);
Put(ocx, OCX_DIAG, "\n\n");
Fail(ocx, 0, "Stopping after parameter query.\n");
}
l = q - arg;
TAILQ_FOREACH(pt, &param_tbl, list) {
if (strlen(pt->name) != l)
continue;
if (!strncmp(pt->name, arg, l))
break;
}
if (pt == NULL)
Fail(ocx, 0, "-p unknown parameter '%.*s' (try -p '?')",
(int)(q - arg), arg);
r = NULL;
d = strtod(q + 1, &r);
if (*r != '\0')
Fail(ocx, 0, "-p '%.*s' bad value '%s'\n",
(int)(q - arg), arg, q + 1);
if (d < pt->min)
Fail(ocx, 0, "-p '%.*s' below min value (%g)\n",
(int)(q - arg), arg, pt->min);
if (d > pt->max)
Fail(ocx, 0, "-p '%.*s' above max value (%g)\n",
(int)(q - arg), arg, pt->max);
Put(ocx, OCX_DIAG, "# Tweak(%s -> %.3e)\n", arg, d);
*(pt->val) = d;
return;
}
void
Param_Report(struct ocx *ocx, enum ocx_chan chan)
{
struct param_tbl *pt;
TAILQ_FOREACH(pt, &param_tbl, list)
Put(ocx, chan, "# param %s %g # min %g, max %g, default %g\n",
pt->name, *pt->val, pt->min, pt->max, pt->def);
}

49
param_instance.h Normal file
View File

@ -0,0 +1,49 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Instantiate a set of parameters
* ===============================
*
* The #including file must #define the desired parameters from param_tbl.h
* into existence (PARAM_INSTANCE), and #define the desired name
* of the local parameter table (PARAM_TABLE_NAME), then include this
* file.
*/
#define PARAM_INSTANCE(nam, vmin, vmax, vdef, docs) \
static double param_##nam = vdef;
#include "param_tbl.h"
#undef PARAM_INSTANCE
/*lint -save -e785 */
static struct param_tbl PARAM_TABLE_NAME[] = {
#define PARAM_INSTANCE(nam, vmin, vmax, vdef, docs) \
{ .name = #nam, .val = &param_##nam, \
.min = vmin, .max = vmax, .def = vdef, .doc = docs },
#include "param_tbl.h"
#undef PARAM_INSTANCE
{ .name = 0 }
};
/*lint -restore */

115
param_tbl.h Normal file
View File

@ -0,0 +1,115 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Parameter Tables
* ================
*
* All parameters tweakable with a -p argument is defined in this file
* including their minimum, maximum and default values and a not too
* terse documentation string.
*
* Things get instantiated from here by #include param_instance.h
*/
/*lint -save -e525 -e539 */
/* name, min, max, default, docs */
#ifdef PARAM_CLIENT
PARAM_CLIENT(poll_rate, 16.0, 4096.0, 64.0, "")
PARAM_CLIENT(foo, 16.0, 4096.0, 64.0, "")
#endif
#ifdef PARAM_NTP_FILTER
PARAM_NTP_FILTER(ntp_filter_average,
3, 1e3, 20,
"Exponential average divisor for average packet delays."
" The value chosen is a compromise between gliding through congestion"
"of common durations and reacting to large-scale routing changes"
"in a timely manner."
" In reality there is no way to tell the two apart."
)
PARAM_NTP_FILTER(ntp_filter_threshold,
0.01, 10.0, 3.00,
"Packet delays exceeding the average by this factor are untrustworthy."
" Setting this too high increases noise from (mild) congestion."
" Setting it too low throws away adequate timestamps."
)
#endif
/**********************************************************************
* Parameters for pll_std.c
*/
#ifdef PARAM_PLL_STD
PARAM_PLL_STD(pll_std_p_init,
1e-3, 0.50, 0.33,
"Proportional term when PLL starts.\n\n"
"Reducing this will make the PLL more resistant to measurement"
" noise and jitter, but also makes it converge slower."
" Increasing this will allmost certainly cause oscillation."
)
PARAM_PLL_STD(pll_std_i_init,
10, 1000, 60,
"Initial P/I ratio when PLL starts.\n\n"
"Reducing this speed up convergence, but risk overshoot."
" Increasing this will slow convergence and reduce impact of noise."
)
PARAM_PLL_STD(pll_std_capture_time,
20, 1e6, 300,
"Capture time before stiffning PLL.\n\n"
"After this many seconds, the PLL will start to stiffen the"
" P and I terms to gain noise immunity."
" Decreasing risks that initial frequency capture is not finished,"
" which will increase the offset-excursion."
" Increasing just delays this stiffning."
)
PARAM_PLL_STD(pll_std_stiffen_rate,
0.5, 1.0, 0.999,
"Rate of PLL P/I term stiffning.\n\n"
"The exponential stiffening per second of D and I terms."
" Decreasing makes stiffening faster."
" Increasing makes stiffening slower."
)
PARAM_PLL_STD(pll_std_p_limit,
1e-6, 0.50, 3e-2,
"Lower limit for Proportional term.\n\n"
"Reducing this will make the PLL more resistent to noise,"
" but going to far it will not be able to steer the clock fast enough."
" Increasing this makes the PLL more agile and prone to noise."
)
#endif
/*lint -restore */

160
pll_std.c Normal file
View File

@ -0,0 +1,160 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Standard PLL
* ============
*
* (And the function pointer for accessing any PLL)
*/
#include <math.h>
#include "ntimed.h"
#define PARAM_PLL_STD PARAM_INSTANCE
#define PARAM_TABLE_NAME pll_std_param_table
#include "param_instance.h"
#undef PARAM_TABLE_NAME
#undef PARAM_PLL_STD
static double pll_integrator;
static struct timestamp pll_last_time;
static int pll_mode;
static double pll_a, pll_b;
static struct timestamp pll_t0;
static void __match_proto__(pll_f)
pll_std(struct ocx *ocx, double offset, double weight)
{
double p_term, dur, dt, rt;
double used_a, used_b;
struct timestamp t0;
TB_Now(&t0);
p_term = 0.0;
dur = .0;
dt = 0;
used_a = used_b = 0;
switch (pll_mode) {
case 0: /* Startup */
pll_t0 = t0;
pll_mode = 1;
pll_a = param_pll_std_p_init;
pll_b = 0.0;
break;
case 1: /* Wait until we have a good estimate, then step */
rt = TS_Diff(&t0, &pll_t0);
if (rt > 2.0 && weight > 3) { // XXX param
if (fabs(offset) > 1e-3) // XXX param
TB_Step(ocx, -offset);
pll_mode = 2;
pll_t0 = t0;
}
break;
case 2: /* Wait for another good estimate, then PLL */
rt = TS_Diff(&t0, &pll_t0);
if (rt > 6.0) {
pll_b = pll_a / param_pll_std_i_init;
pll_t0 = t0;
pll_mode = 3;
}
break;
case 3: /* track mode */
rt = TS_Diff(&t0, &pll_t0);
assert(rt > 0);
dt = TS_Diff(&t0, &pll_last_time);
assert(dt > 0);
/*
* XXX: Brute-force exploitation of the weight.
*
* Ideally, we should scale the pll_[ab] terms and the
* stiffening of them based on the weight. That is harder
* than it sounds -- or at least I have not found a good
* candidate function yet.
* In the meantime this is a simple threshold based
* prevention of horribly distant servers injecting too
* much noise into the very reactive default PLL.
* Some averaging of the weight may be required.
*/
if (weight < 50) {
used_a = 3e-2;
used_b = 5e-4;
} else if (weight < 150) {
used_a = 6e-2;
used_b = 1e-3;
} else {
if (rt > param_pll_std_capture_time &&
pll_a > param_pll_std_p_limit) {
pll_a *= pow(param_pll_std_stiffen_rate, dt);
pll_b *= pow(param_pll_std_stiffen_rate, dt);
}
used_a = pll_a;
used_b = pll_b;
}
p_term = -offset * used_a;
pll_integrator += p_term * used_b;
dur = dt;
break;
default:
WRONG("Wrong PLL state");
}
dur = ceil(dur);
/* Clamp (XXX: leave to timebase to do this ?) */
if (p_term > dur * 500e-6)
p_term = dur * 500e-6;
if (p_term < dur * -500e-6)
p_term = dur * -500e-6;
pll_last_time = t0;
Put(ocx, OCX_TRACE,
"PLL %d %.3e %.3e %.3e -> %.3e %.3e %.3e %.3e %.3e\n",
pll_mode, dt, offset, weight,
p_term, dur, pll_integrator,
used_a, used_b);
if (dur > 0.0)
TB_Adjust(ocx, p_term, dur, pll_integrator);
}
pll_f *PLL = NULL;
void
PLL_Init(void)
{
Param_Register(pll_std_param_table);
PLL = pll_std;
}

163
plotgen.py Normal file
View File

@ -0,0 +1,163 @@
#!/usr/bin/env python
#
# Copyright (c) 2014 Poul-Henning Kamp
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# Postprocess simulation output for gnuplot
# =========================================
#
# python plotgen.py
# then
# gnuplot
# load '/tmp/_g'
#
from __future__ import print_function
fi = open("/tmp/_")
fg = open("/tmp/_g", "w")
fg.write("#\n")
plots = []
class plot(object):
def __init__(self, title = "-"):
self.s = []
self.title = title
plots.append(self)
def write(self, l):
self.s.append(l)
def commit(self, fg, title):
if title:
fg.write("set y2label '%s'\n" % self.title)
else:
fg.write("unset y2label\n")
for i in self.s:
fg.write(i + "\n")
now = 0.0
class simpll(object):
def __init__(self):
self.fo = open("/tmp/_simpll", "w")
self.pl1 = plot('p/freq')
self.pl1.write(
"plot '/tmp/_simpll' using 1:($2*1e6) with line notitle")
self.pl2 = plot('p/off')
self.pl2.write(
"plot '/tmp/_simpll' using 1:($3*1e6/($4 == 0 ? 1 : $4)) with impulse notitle")
if False:
self.pl3 = plot('p/dur')
self.pl3.write(
"plot '/tmp/_simpll' using 1:4 with imp notitle")
def data(self, ll):
self.fo.write("%f " % now + " " + " ".join(ll[1:]) + "\n")
class combine(object):
def __init__(self):
self.fo = open("/tmp/_combine", "w")
self.pl1 = plot("c/in")
self.pl1.write(
"plot '/tmp/_combine' using 1:2 notitle"
", '/tmp/_combine' using 1:4 notitle"
", '/tmp/_combine' using 1:3 notitle"
)
self.pl2 = plot('c/peak')
self.pl2.write(
"plot '/tmp/_combine' using 1:5 with line notitle"
)
self.pl3 = plot("c/weight")
self.pl3.write("set yrange[0:]")
self.pl3.write(
"plot '/tmp/_combine' using 1:6 axis x1y2 with line notitle"
)
self.pl3.write("set autoscale y")
def data(self, ll):
self.fo.write("%f " % now + " " + " ".join(ll[3:]) + "\n")
p_simpll = simpll()
p_combine = combine()
n = 0
for l in fi:
n += 1
if len(l) == 0 or l[0] == "#":
continue
ll = l.split()
if ll[0] == "Now":
now = float(ll[1]) - 1e6
continue
if ll[0] == "SIMPLL":
p_simpll.data(ll)
continue
if ll[0] == "Combine":
p_combine.data(ll)
continue
def plotset(xlo, xhi, lbl):
fg.write("set autoscale x\n")
fg.write("set xrange [%g:%g]\n" % (xlo, xhi))
fg.write("set bmargin 0\n")
fg.write("set xtics format ''\n")
fg.write("set xtics (%g, %g)\n" % (xlo, xhi))
for i in plots[:-1]:
i.commit(fg, lbl)
fg.write("set bmargin 2\n")
fg.write("set xtics format '%g'\n")
fg.write("set xtics (%g, %g)\n" % (xlo, xhi))
plots[-1].commit(fg, lbl)
fg.write("set grid\n")
fg.write("set pointsize .5\n")
fg.write("set size .8,.8\n")
fg.write("set tmargin 1\n")
fg.write("set lmargin 8\n")
fg.write("set multiplot layout %d,3 columnsfirst\n" % len(plots))
fg.write("set style data point\n")
plotset(0, 64, False)
if now > 10000:
fg.write("set pointsize .2\n")
plotset(60, 3600, False)
fg.write("set pointsize .1\n")
plotset(3600, now, True)
else:
fg.write("set pointsize .5\n")
plotset(60, 600, False)
fg.write("set pointsize .2\n")
plotset(5, now, True)
fg.write("unset multiplot\n")
fg.write("set autoscale\n")
fg.write("set xtics auto\n")
fg.write("set ytics auto\n")

178
time_sim.c Normal file
View File

@ -0,0 +1,178 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Simulated timebase
* ==================
*
* Very simple minded: Time advances when TB_Sleep() is called only.
*
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "ntimed.h"
static struct timestamp st_now;
static double freq = 0;
static double freq0 = 0;
static double adj_offset = 0;
static double adj_duration = 0;
static double adj_freq = 0;
/*
* This variable is public and represent the amount of time the simulated
* clock has been tweaked by the TB_Step() and TB_Adjust() functions.
*
* This can be used to "(re-)model" previously recorded event series
* onto the ST timebase.
*/
double Time_Sim_delta = 0;
/**********************************************************************/
static struct timestamp *
st_Now(struct timestamp *storage)
{
if (storage == NULL)
ALLOC_OBJ(storage, TIMESTAMP_MAGIC);
AN(storage);
*storage = st_now;
return (storage);
}
/**********************************************************************/
static void
st_Sleep(double dur)
{
TS_Add(&st_now, dur);
Time_Sim_delta += dur * freq;
}
/**********************************************************************/
static void __match_proto__(tb_step_f)
st_Step(struct ocx *ocx, double offset)
{
Debug(ocx, "SIMSTEP %.3e\n", offset);
Time_Sim_delta += offset;
TB_generation++;
}
/**********************************************************************/
static void __match_proto__(tb_adjust_f)
st_Adjust(struct ocx *ocx, double offset, double duration, double frequency)
{
(void)ocx;
adj_offset = offset;
adj_duration = floor(duration);
if (adj_offset > 0.0 && adj_duration == 0.0)
adj_duration = 1.0;
adj_freq = frequency;
}
/**********************************************************************/
static enum todo_e __match_proto__(todo_f)
st_kern_pll(struct ocx *ocx, struct todolist *tdl, void *priv)
{
double d;
(void)ocx;
AN(tdl);
AZ(priv);
freq = freq0 + adj_freq;
if (adj_duration > 0.0) {
d = adj_offset / adj_duration;
freq += d;
adj_offset -= d;
adj_duration -= 1.0;
}
Put(ocx, OCX_TRACE, "SIMPLL %.3e %.3e %.3e\n",
adj_freq, adj_offset, adj_duration);
return (TODO_OK);
}
/**********************************************************************
* Mechanism to artificially bump simulated clock around.
*/
struct st_bump {
unsigned magic;
#define ST_BUMP_MAGIC 0xc8981be3
double bfreq;
double bphase;
};
static enum todo_e __match_proto__(todo_f)
st_kern_bump(struct ocx *ocx, struct todolist *tdl, void *priv)
{
struct st_bump *stb;
(void)ocx;
(void)tdl;
CAST_OBJ_NOTNULL(stb, priv, ST_BUMP_MAGIC);
freq0 += stb->bfreq;
Time_Sim_delta += stb->bphase;
FREE_OBJ(stb);
return (TODO_OK);
}
void
Time_Sim_Bump(struct todolist *tdl, double when, double bfreq, double bphase)
{
struct st_bump *stb;
ALLOC_OBJ(stb, ST_BUMP_MAGIC);
AN(stb);
stb->bfreq = bfreq;
stb->bphase = bphase;
(void)TODO_ScheduleRel(tdl, st_kern_bump, stb, when, 0.0, "BUMP");
}
/**********************************************************************/
void
Time_Sim(struct todolist *tdl)
{
INIT_OBJ(&st_now, TIMESTAMP_MAGIC);
TS_Add(&st_now, 1e6);
TB_Now = st_Now;
TB_Sleep = st_Sleep;
TB_Step = st_Step;
TB_Adjust = st_Adjust;
(void)TODO_ScheduleRel(tdl, st_kern_pll, NULL, 0.0, 1.0, "SIMPLL");
}

261
time_stuff.c Normal file
View File

@ -0,0 +1,261 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Timebase infrastructure
* =======================
*
* This file implements the generic timebase stuff, calling out to a specific
* implementation through the function pointers as required.
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ntimed.h"
#define NANO_FRAC 18446744074ULL // 2^64 / 1e9
/*
* Whenever the clock is stepped, we increment this generation number.
*
* XXX: Add support for stepping externally via a signal (SIGRESUME ?)
*/
int TB_generation = 41;
/**********************************************************************/
static struct timestamp *
ts_fixstorage(struct timestamp *storage)
{
if (storage == NULL) {
ALLOC_OBJ(storage, TIMESTAMP_MAGIC);
AN(storage);
} else {
AN(storage);
memset(storage, 0, sizeof *storage);
storage->magic = TIMESTAMP_MAGIC;
}
return (storage);
}
/**********************************************************************/
struct timestamp *
TS_Nanosec(struct timestamp *storage, intmax_t sec, intmax_t nsec)
{
storage = ts_fixstorage(storage);
assert(sec >= 0);
assert(nsec >= 0);
assert(nsec < 1000000000);
storage->sec = (uint64_t)sec;
storage->frac = (uint32_t)nsec * NANO_FRAC;
return (storage);
}
/**********************************************************************/
struct timestamp *
TS_Double(struct timestamp *storage, double d)
{
assert(d >= 0.0);
storage = ts_fixstorage(storage);
storage->sec += (uint64_t)floor(d);
d -= floor(d);
storage->frac = (uint64_t)ldexp(d, 64);
return (storage);
}
/**********************************************************************/
void
TS_Add(struct timestamp *ts, double dt)
{
double di;
CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC);
dt += ldexp(ts->frac, -64);
di = floor(dt);
ts->sec += (uint64_t)di;
ts->frac = (uint64_t)ldexp(dt - di, 64);
}
/**********************************************************************/
double
TS_Diff(const struct timestamp *t1, const struct timestamp *t2)
{
double d;
CHECK_OBJ_NOTNULL(t1, TIMESTAMP_MAGIC);
CHECK_OBJ_NOTNULL(t2, TIMESTAMP_MAGIC);
d = ldexp((double)t1->frac - (double)t2->frac, -64);
d += ((double)t1->sec - (double)t2->sec);
return (d);
}
/**********************************************************************/
void
TS_SleepUntil(const struct timestamp *t)
{
struct timestamp now;
double dt;
TB_Now(&now);
dt = TS_Diff(t, &now);
if (dt > 0.)
TB_Sleep(dt);
}
/**********************************************************************/
void
TS_Format(char *buf, ssize_t len, const struct timestamp *ts)
{
CHECK_OBJ_NOTNULL(ts, TIMESTAMP_MAGIC);
uint64_t x, y;
/* XXX: Nanosecond precision is enough for everybody. */
x = ts->sec;
y = (ts->frac + NANO_FRAC / 2ULL) / NANO_FRAC;
if (y >= 1000000000ULL) {
y -= 1000000000ULL;
x += 1;
}
assert(snprintf(buf, len, "%jd.%09jd", x, y) < len);
}
/**********************************************************************
* DUMMY TimeBase functions
*/
static struct timestamp *
tb_Now(struct timestamp *storage)
{
(void)storage;
WRONG("No TB_Now");
return (NULL);
}
tb_now_f *TB_Now = tb_Now;
/**********************************************************************/
static void
tb_Sleep(double dur)
{
(void)dur;
WRONG("No TB_Sleep");
}
tb_sleep_f *TB_Sleep = tb_Sleep;
/**********************************************************************/
static void __match_proto__(tb_step_f)
tb_Step(struct ocx *ocx, double offset)
{
(void)ocx;
(void)offset;
WRONG("No TB_Step");
}
tb_step_f *TB_Step = tb_Step;
/**********************************************************************/
static void __match_proto__(tb_adjust_f)
tb_Adjust(struct ocx *ocx, double offset, double duration, double frequency)
{
(void)ocx;
(void)offset;
(void)duration;
(void)frequency;
WRONG("No TB_Adjust");
}
tb_adjust_f *TB_Adjust = tb_Adjust;
/**********************************************************************
* Timebase test functions.
*/
static int
ts_onetest(struct ocx *ocx, const struct timestamp *ts, double off)
{
struct timestamp ts2;
double dt;
char buf[40];
TS_Format(buf, sizeof buf, ts);
ts2 = *ts;
TS_Add(&ts2, off);
Debug(ocx, "%s + %12.9f = ", buf, off);
TS_Format(buf, sizeof buf, &ts2);
dt = TS_Diff(&ts2, ts) - off;
Debug(ocx, "%s %8.1e", buf, dt);
if (fabs(dt) > 5e-10) {
Debug(ocx, " ERR\n");
return (1);
}
Debug(ocx, " OK\n");
return (0);
}
void
TS_RunTest(struct ocx *ocx)
{
struct timestamp ts;
int nf = 0;
TB_Now(&ts);
nf += ts_onetest(ocx, &ts, 1e-9);
nf += ts_onetest(ocx, &ts, 1e-8);
nf += ts_onetest(ocx, &ts, 1e-6);
nf += ts_onetest(ocx, &ts, 1e-3);
nf += ts_onetest(ocx, &ts, 1e-1);
nf += ts_onetest(ocx, &ts, 0.999);
nf += ts_onetest(ocx, &ts, 1.001);
nf += ts_onetest(ocx, &ts, 1.999);
nf += ts_onetest(ocx, &ts, -2.000);
nf += ts_onetest(ocx, &ts, -1.999);
nf += ts_onetest(ocx, &ts, -1.000);
nf += ts_onetest(ocx, &ts, -0.999);
nf += ts_onetest(ocx, &ts, -1e-3);
nf += ts_onetest(ocx, &ts, -1e-6);
nf += ts_onetest(ocx, &ts, -1e-9);
Debug(ocx, "TS_RunTest: %d failures\n", nf);
AZ(nf);
}

256
time_unix.c Normal file
View File

@ -0,0 +1,256 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* UNIX timebase
* =============
*
* Implement the timebase functions on top of a modern UNIX kernel which
* has the some version of the Mills/Kamp kernel PLL code and either
* [gs]ettimeofday(2) or better: clock_[gs]ettime(2) API.
*
*/
#include <errno.h>
#include <math.h>
#include <poll.h>
#include <string.h>
#include <sys/time.h>
#include <sys/timex.h>
#include "ntimed.h"
static double adj_offset = 0;
static double adj_duration = 0;
static double adj_freq = 0;
// #undef CLOCK_REALTIME /* Test old unix code */
/**********************************************************************
* The NTP-pll in UNIX kernels apply the offset correction in an
* exponential-decay fashion for historical and wrong reasons.
*
* The short explanation is that this ends up confusing all PLLs I have
* ever seen, by introducing mainly odd harmonics of the PLL update period
* into all time-measurements in the system.
*
* A much more sane mode would be to tell the kernel "I want this much
* offset accumulated over this many seconds", giving a constant frequency
* over the PLL update period while still falling back to the frequency
* estimate should the time-steering userland process fail.
*
* I will add such a mode to the FreeBSD kernel as a reference implementation
* at a later date, in the mean time this code implements it by updating the
* kernel frequency from userland as needed.
*
* XXX: Optimise to only wake up when truly needed, rather than every second.
* XXX: Requires TODO cancellation.
*/
static void
kt_setfreq(double frequency)
{
struct timex tx;
int i;
assert(isfinite(frequency));
memset(&tx, 0, sizeof tx);
tx.modes = MOD_STATUS;
#if defined(MOD_NANO)
tx.modes |= MOD_NANO;
#elif defined(MOD_MICRO)
tx.modes |= MOD_MICRO;
#endif
tx.status = STA_PLL | STA_FREQHOLD;
tx.modes = MOD_FREQUENCY;
tx.freq = (long)floor(frequency * (65536 * 1e6));
errno = 0;
i = ntp_adjtime(&tx);
/* XXX: what is the correct error test here ? */
assert(i >= 0);
}
static enum todo_e __match_proto__(todo_f)
kt_pll(struct ocx *ocx, struct todolist *tdl, void *priv)
{
double d, freq;
struct timestamp ts;
char buf[40];
(void)ocx;
AN(tdl);
AZ(priv);
freq = adj_freq;
if (adj_duration > 0.0) {
d = adj_offset / adj_duration;
freq += d;
adj_offset -= d;
adj_duration -= 1.0;
}
TB_Now(&ts);
TS_Format(buf, sizeof buf, &ts);
Put(ocx, OCX_TRACE, "KERNTIME_PLL %s %.6e %.6e %.3e %.6e\n",
buf, adj_freq, adj_offset, adj_duration, freq);
kt_setfreq(freq);
return (TODO_OK);
}
static void __match_proto__(tb_adjust_f)
kt_adjust(struct ocx *ocx, double offset, double duration, double frequency)
{
(void)ocx;
adj_offset = offset;
adj_duration = floor(duration);
if (adj_offset > 0.0 && adj_duration == 0.0)
adj_duration = 1.0;
adj_freq = frequency;
}
/**********************************************************************/
#ifdef CLOCK_REALTIME
static void __match_proto__(tb_step_f)
kt_step(struct ocx *ocx, double offset)
{
double d;
struct timespec ts;
Put(ocx, OCX_TRACE, "KERNTIME_STEP %.3e\n", offset);
d = floor(offset);
offset -= d;
AZ(clock_gettime(CLOCK_REALTIME, &ts));
ts.tv_sec += (long)d;
ts.tv_nsec += (long)floor(offset * 1e9);
if (ts.tv_nsec < 0) {
ts.tv_sec -= 1;
ts.tv_nsec += 1000000000;
} else if (ts.tv_nsec >= 1000000000) {
ts.tv_sec += 1;
ts.tv_nsec -= 1000000000;
}
AZ(clock_settime(CLOCK_REALTIME, &ts));
TB_generation++;
}
#else
static void __match_proto__(tb_step_f)
kt_step(struct ocx *ocx, double offset)
{
double d;
struct timeval tv;
Put(ocx, OCX_TRACE, "KERNTIME_STEP %.3e\n", offset);
d = floor(offset);
offset -= d;
AZ(gettimeofday(&tv, NULL));
tv.tv_sec += (long)d;
tv.tv_usec += (long)floor(offset * 1e6);
if (tv.tv_usec < 0) {
tv.tv_sec -= 1;
tv.tv_usec += 1000000;
} else if (tv.tv_usec >= 1000000) {
tv.tv_sec += 1;
tv.tv_usec -= 1000000;
}
AZ(settimeofday(&tv, NULL));
TB_generation++;
}
#endif
/**********************************************************************/
#if defined (CLOCK_REALTIME)
static struct timestamp * __match_proto__(tb_now_f)
kt_now(struct timestamp *storage)
{
struct timespec ts;
AZ(clock_gettime(CLOCK_REALTIME, &ts));
return (TS_Nanosec(storage, ts.tv_sec, ts.tv_nsec));
}
#else
static struct timestamp * __match_proto__(tb_now_f)
kt_now(struct timestamp *storage)
{
struct timeval tv;
AZ(gettimeofday(&tv, NULL));
return (TS_Nanosec(storage, tv.tv_sec, tv.tv_usec * 1000LL));
}
#endif
/**********************************************************************/
static void __match_proto__(tb_sleep_f)
kt_sleep(double dur)
{
struct pollfd fds[1];
AZ(poll(fds, 0, (int)floor(dur * 1e3)));
}
/**********************************************************************/
void
Time_Unix(struct todolist *tdl)
{
struct timestamp ts;
TB_Step = kt_step;
TB_Adjust = kt_adjust;
TB_Sleep = kt_sleep;
TB_Now = kt_now;
/* Aim the userland PLL wrangler at 125msec before the second */
TB_Now(&ts);
ts.sec += 1;
ts.frac = 14ULL << 60ULL;
(void)TODO_ScheduleAbs(tdl, kt_pll, NULL, &ts, 1.0, "SIMPLL");
}
/**********************************************************************
* Non-tweaking subset.
*/
void
Time_Unix_Passive(void)
{
TB_Sleep = kt_sleep;
TB_Now = kt_now;
}

175
todo.c Normal file
View File

@ -0,0 +1,175 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* todo-list scheduler
* ===================
*
* This is a simple "TODO-list" scheduler for calling things at certain
* times. Jobs can be one-shot or repeated and repeated jobs can abort.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "ntimed.h"
struct todo {
unsigned magic;
#define TODO_MAGIC 0x5279009a
TAILQ_ENTRY(todo) list;
todo_f *func;
void *priv;
struct timestamp when;
double repeat;
char what[40];
};
struct todolist {
unsigned magic;
#define TODOLIST_MAGIC 0x7db66255
TAILQ_HEAD(,todo) todolist;
};
struct todolist *
TODO_NewList(void)
{
struct todolist *tdl;
ALLOC_OBJ(tdl, TODOLIST_MAGIC);
AN(tdl);
TAILQ_INIT(&tdl->todolist);
return (tdl);
}
static void
todo_insert(struct todolist *tdl, struct todo *tp)
{
struct todo *tp1;
TAILQ_FOREACH(tp1, &tdl->todolist, list) {
if (TS_Diff(&tp1->when, &tp->when) > 0.0) {
TAILQ_INSERT_BEFORE(tp1, tp, list);
return;
}
}
TAILQ_INSERT_TAIL(&tdl->todolist, tp, list);
}
/**********************************************************************
* Return a pointer to the todo item, so the API can support cancellation.
* (Cancellation will be necessary for NTP-peer removal)
*
* For ease of debugging, TODO jobs have a name.
*/
struct todo *
TODO_ScheduleAbs(struct todolist *tdl, todo_f *func, void *priv,
const struct timestamp *when, double repeat, const char *fmt, ...)
{
struct todo *tp;
va_list ap;
CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC);
AN(func);
CHECK_OBJ_NOTNULL(when, TIMESTAMP_MAGIC);
assert(repeat >= 0.0);
AN(fmt);
ALLOC_OBJ(tp, TODO_MAGIC);
AN(tp);
tp->func = func;
tp->priv = priv;
tp->when = *when;
tp->repeat = repeat;
va_start(ap, fmt);
(void)vsnprintf(tp->what, sizeof tp->what, fmt, ap);
va_end(ap);
todo_insert(tdl, tp);
return (tp);
}
struct todo *
TODO_ScheduleRel(struct todolist *tdl, todo_f *func, void *priv,
double when, double repeat, const char *fmt, ...)
{
struct todo *tp;
va_list ap;
CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC);
AN(func);
assert(when >= 0.0);
assert(repeat >= 0.0);
AN(fmt);
ALLOC_OBJ(tp, TODO_MAGIC);
AN(tp);
tp->func = func;
tp->priv = priv;
TB_Now(&tp->when);
TS_Add(&tp->when, when);
tp->repeat = repeat;
va_start(ap, fmt);
(void)vsnprintf(tp->what, sizeof tp->what, fmt, ap);
va_end(ap);
todo_insert(tdl, tp);
return (tp);
}
/**********************************************************************
* Schedule TODO list until failure or empty
*/
enum todo_e
TODO_Run(struct ocx *ocx, struct todolist *tdl)
{
struct todo *tp;
enum todo_e ret = TODO_OK;
char buf[40];
CHECK_OBJ_NOTNULL(tdl, TODOLIST_MAGIC);
while(!TAILQ_EMPTY(&tdl->todolist)) {
tp = TAILQ_FIRST(&tdl->todolist);
TS_SleepUntil(&tp->when);
TS_Format(buf, sizeof buf, &tp->when);
Put(ocx, OCX_TRACE, "Now %s %s\n", buf, tp->what);
ret = tp->func(ocx, tdl, tp->priv);
if (ret == TODO_FAIL)
break;
if (ret == TODO_DONE || tp->repeat == 0.0) {
TAILQ_REMOVE(&tdl->todolist, tp, list);
FREE_OBJ(tp);
} else if (ret == TODO_OK) {
TS_Add(&tp->when, tp->repeat);
TAILQ_REMOVE(&tdl->todolist, tp, list);
todo_insert(tdl, tp);
} else {
WRONG("Invalid Return from todo->func");
}
}
return (ret);
}

131
udp.c Normal file
View File

@ -0,0 +1,131 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <string.h>
#include <sys/socket.h>
#include "ntimed.h"
#include "udp.h"
int
UdpTimedSocket(struct ocx *ocx, int fam)
{
int fd;
int i;
fd = socket(fam, SOCK_DGRAM, 0);
if (fd < 0)
Fail(ocx, 1, "socket(2) failed");
#ifdef IP_RECVDSTADDR
i = 1;
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &i, sizeof i) != 0) {
AZ(close(fd));
Fail(ocx, 1, "setsockopt(IP_RECVDSTADDR) failed");
}
#endif
#ifdef SO_TIMESTAMPNS
i = 1;
(void)setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &i, sizeof i);
#elif defined(SO_TIMESTAMP)
i = 1;
(void)setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &i, sizeof i);
#endif
return (fd);
}
ssize_t
UdpTimedRx(struct ocx *ocx, int fd, struct sockaddr_storage *ss, socklen_t *sl,
struct timestamp *ts, void *buf, ssize_t len)
{
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsg;
u_char ctrl[1024];
ssize_t rl;
assert(fd >= 0);
AN(ss);
AN(sl);
AN(ts);
AN(buf);
assert(len > 0);
memset(&msg, 0, sizeof msg);
msg.msg_name = (void*)ss;
msg.msg_namelen = sizeof *ss;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = ctrl;
msg.msg_controllen = sizeof ctrl;
iov.iov_base = buf;
iov.iov_len = (size_t)len;
memset(ctrl, 0, sizeof ctrl);
cmsg = (void*)ctrl;
rl = recvmsg(fd, &msg, 0);
if (rl <= 0)
return (rl);
*sl = msg.msg_namelen;
for(;cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
#ifdef SCM_TIMESTAMPNS
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_TIMESTAMPNS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) {
struct timespec tsc;
memcpy(&tsc, CMSG_DATA(cmsg), sizeof tsc);
(void)TS_Nanosec(ts, tsc.tv_sec, tsc.tv_nsec);
continue;
}
#endif
#ifdef SCM_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) {
struct timeval tv;
memcpy(&tv, CMSG_DATA(cmsg), sizeof tv);
(void)TS_Nanosec(ts, tv.tv_sec, tv.tv_usec * 1000LL);
continue;
}
#endif
#ifdef IP_RECVDSTADDR
if (cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_RECVDSTADDR &&
cmsg->cmsg_len == CMSG_LEN(sizeof(in_addr_t))) {
continue;
/* XXX */
}
#endif
Debug(ocx, "RX-msg: %d %d %u ",
cmsg->cmsg_level, cmsg->cmsg_type, cmsg->cmsg_len);
DebugHex(ocx, CMSG_DATA(cmsg), cmsg->cmsg_len);
Debug(ocx, "\n");
}
return (rl);
}

39
udp.h Normal file
View File

@ -0,0 +1,39 @@
/*-
* Copyright (c) 2014 Poul-Henning Kamp
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifdef UDP_H_INCLUDED
#error "udp.h included multiple times"
#endif
#define UDP_H_INCLUDED
/**********************************************************************
* UDP sockets
*/
int UdpTimedSocket(struct ocx *ocx, int fam);
ssize_t UdpTimedRx(struct ocx *ocx, int fd, struct sockaddr_storage *ss,
socklen_t *sl, struct timestamp *ts, void *buf, ssize_t len);