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:
218
README.rst
Normal file
218
README.rst
Normal 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
216
combine_delta.c
Normal 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
144
configure
vendored
Normal 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
74
main.c
Normal 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
115
main_client.c
Normal 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
160
main_poll_server.c
Normal 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
297
main_sim_client.c
Normal 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
179
ntimed.h
Normal 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
71
ntimed_endian.h
Normal 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
186
ntimed_queue.h
Normal 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
162
ntimed_tricks.h
Normal 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
137
ntp.h
Normal 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
192
ntp_filter.c
Normal 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
183
ntp_packet.c
Normal 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
178
ntp_peer.c
Normal 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
237
ntp_peerset.c
Normal 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
49
ntp_tbl.h
Normal 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
201
ntp_tools.c
Normal 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
172
ocx_stdio.c
Normal 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
160
param.c
Normal 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(¶m_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, ¶m_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, ¶m_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, ¶m_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, ¶m_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
49
param_instance.h
Normal 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 = ¶m_##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
115
param_tbl.h
Normal 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
160
pll_std.c
Normal 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
163
plotgen.py
Normal 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
178
time_sim.c
Normal 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
261
time_stuff.c
Normal 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
256
time_unix.c
Normal 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
175
todo.c
Normal 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
131
udp.c
Normal 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
39
udp.h
Normal 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);
|
||||
|
||||
Reference in New Issue
Block a user