Compare commits

...

345 Commits

Author SHA1 Message Date
Miroslav Lichvar
554b9b06de doc: update NEWS 2017-07-25 17:54:01 +02:00
Miroslav Lichvar
f734bd1a7c sys_linux: allow getrandom in seccomp filter
This fixes commit c5735ebfe9.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
77fc5c42b9 client: don't allow slash with hostname in allow/deny command 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
ea85bc43e0 conf: don't allow slash with hostname in allow/deny directive 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
e8fb11c433 reference: don't report zero stratum when synchronised
If synchronised to a stratum 15 source, return stratum of 16 instead of
0 in the tracking report. It will not match the value in server mode
packets, but it should be less confusing.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
01a29c7a11 cmdmon: report offset after manual timestamp as float
Modify the protocol to report the offset as seconds in floating point
instead of integer number of centiseconds.
2017-07-25 17:40:35 +02:00
Miroslav Lichvar
6ec3dc1650 manual: handle failed robust regression 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
0c54cf316d util: avoid undefined behavior in timestamp conversion 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
bd3fb49a1e client: avoid undefined bit shifts 2017-07-25 17:40:35 +02:00
Miroslav Lichvar
f6e72a80e1 regress: avoid undefined behavior in pointer arithmetic 2017-07-21 17:14:15 +02:00
Miroslav Lichvar
c2ab1426e5 ntp: simplify get_poll_adj() 2017-07-21 16:27:03 +02:00
Miroslav Lichvar
fa2c59d78d sourcestats: increase number of samples needed to check delay
Require at least 6 samples to check the increase in the delay of a new
sample to make it more reliable.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
16afa8eb50 ntp: don't accumulate old samples in interleaved client mode
Check how many responses were missing before accumulating a sample using
old timestamps to avoid correcting the clock with an offset extrapolated
over a long interval.

This should be eventually done in sourcestats for all sources.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
992590e99c ntp: revert reversed poll tracking in interleaved mode
With the new selection of timestamps in the interleaved mode it's no
longer necessary to reverse the poll tracking in order to reduce the
local and remote intervals of measurements that makes the peer with
higher stratum.

This reverts commit 4a24368763.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
0baa35eade ntp: select timestamps in interleaved mode
Use previous local TX and remote RX timestamps for the new sample in the
interleaved mode if it will make the local and remote intervals
significantly shorter in order to improve the accuracy of the measured
delay.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
2e0870ee0c ntp: refactor timestamp selection and interval calculation
Prepare the code for a third option in the timestamp selection and clean
it up a bit.
2017-07-21 16:27:03 +02:00
Miroslav Lichvar
43cd119d6d ntp: add function for zeroing local timestamps 2017-07-21 16:27:03 +02:00
Miroslav Lichvar
62cd319a51 ntp: fix poll in source report
The source report used the local interval, which in symmetric mode may
be longer than the actual interval used for transmission.
2017-07-14 20:25:50 +02:00
Miroslav Lichvar
d0f789425b ntp: ignore saved remote poll when peer is not responding
When a peer stops responding, allow our actual polling interval to be
longer than poll saved from the last valid response.
2017-07-14 20:06:31 +02:00
Miroslav Lichvar
30e6549692 ntp: reset TX counter on all valid responses
Also change it to an unsigned type.
2017-07-14 19:40:44 +02:00
Miroslav Lichvar
043c7d7c9f configure: fix compiler warning in getrandom() test 2017-07-14 10:21:31 +02:00
Miroslav Lichvar
1c277a8850 configure: check for hardening compiler options
If no CFLAGS are specified, check if common security hardening options
are supported and add them to the CFLAGS/LDFLAGS. These are typically
enabled in downstream packages, but users compiling chrony from sources
with default CFLAGS should get hardened binaries too.
2017-07-13 16:12:25 +02:00
Bryan Christianson
ccb94ac5fb sys_macosx: add support for ntp_adjtime() on macOS 10.13+
macOS 10.13 will implement the ntp_adjtime() system call, allowing
better control over the system clock than is possible with the existing
adjtime() system call. chronyd will support both the older and newer
calls, enabling binary code to run without recompilation on macOS 10.9
through macOS 10.13.

Early releases of macOS 10.13 have a very buggy adjtime() call. The
macOS driver tests adjtime() to see if the bug has been fixed. If the
bug persists then the timex driver is invoked otherwise the netbsd
driver.
2017-07-13 16:10:54 +02:00
Miroslav Lichvar
778fce4039 main: don't require root privileges with -Q option
If the -Q option is specified, disable by default pidfile, ntpport,
cmdport, Unix domain command socket, and clock control, in order to
allow starting chronyd without root privileges and/or when another
chronyd instance is already running.
2017-07-13 16:10:54 +02:00
Miroslav Lichvar
9983185d6d ntp: define NTP port for configuration code 2017-07-13 16:10:54 +02:00
Miroslav Lichvar
7bd1c02781 main: refactor check of pidfile 2017-07-13 16:10:54 +02:00
Miroslav Lichvar
760285218f sys_timex: fix update of TAI offset on non-Linux systems
The tai field in struct timex is a Linux-specific feature. It's possible
to read the current offset with ntp_gettime() (or ntp_gettimex() on
Linux), but apparently not all libc implementations support it.

Rework the code to save and adjust the last value instead of reading
the current value from the kernel.
2017-07-11 11:28:34 +02:00
Miroslav Lichvar
4fe0e6b7fd sys_timex: rename status variable 2017-07-10 14:48:47 +02:00
Miroslav Lichvar
0773a1e630 ntp: fix debug message about unknown HW timestamping ifindex 2017-06-30 17:01:06 +02:00
Miroslav Lichvar
4a24368763 ntp: reverse poll tracking in interleaved symmetric mode
Unlike in the basic mode, the peer with a higher stratum needs to wait
for a response before sending the next request in order to minimize the
delay of the measurement and error in the measured delay.

Slightly increase the delay adjustment to make it work with older chrony
versions.
2017-06-30 17:01:06 +02:00
Miroslav Lichvar
577290c5bc ntp: fix poll interleaving with unsynchronised peers
Update the remote poll and remote stratum even for unsychronised peers,
and handle stratum of 0 as 16, so the peers work with the opposite
differences between their strata and can adjust their polling intervals
in order to interleave the packets.
2017-06-30 17:01:01 +02:00
Miroslav Lichvar
854ff69f78 hwclock: decrease tolerance of robust regression to 0.1 ppb 2017-06-30 16:58:57 +02:00
Miroslav Lichvar
29b0ad894c reference: get TAI-UTC offset from leap second timezone
Use the timezone specified by the leapsectz directive to get the
current TAI-UTC offset and set the offset of the system clock in order
to provide correct TAI time to applications using ntp_adjtime(),
ntp_gettime(), or clock_gettime(CLOCK_TAI).
2017-06-30 16:58:53 +02:00
Miroslav Lichvar
cde0a20307 sys_timex: add support for setting TAI-UTC offset 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
a768578a26 local: add support for setting TAI-UTC offset 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
5d838729ef reference: move static tz variables to function using them 2017-06-30 13:43:35 +02:00
Miroslav Lichvar
d6b763dc24 client: check IP address family before printing as refid 2017-06-30 13:43:27 +02:00
Miroslav Lichvar
95adb52a45 configure: add missing object for PHC refclock
This fixes commit eceb8d9937.
2017-06-27 15:29:02 +02:00
Miroslav Lichvar
707d9a3484 test: add regress unit test 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
1872d4d195 test: fix crash when printing debug messages
This fixes commit 6cbeb107db.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
17f32c266e sourcestats: use median distance in weight calculation
Replace mean distance with median distance in the weight calculation.
This should make the weights less sensitive to outliers.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
6207655ab2 regress: provide function to find median 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
5e1e31ad5f regress: reduce maximum number of points to 64
This corresponds to the maximum number of points used by regress users.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
13111c1dd8 regress: use chars instead of ints for flags
This reduces the size of the flags array on stack.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
85c84073c1 regress: fix assertion in robust regression 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
c2944d8727 regress: speed up range expansion in robust regression
Instead of repeatedly expanding the range of b with the same increment,
double the range on each iteration to speed up the expansion. Also, add
a sanity check for the interval.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
e118b9b1e8 regress: fix robust regression
The bisection always terminated after one iteration. Change the code to
check if the middle is different from the lower and upper limits as
suggested in the original recipe.

This fixes commit b14689d59b.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
7fb7f95979 sourcestats: include precision in weight calculation
In order to stabilize the weights of refclock samples which have only
slightly different distances, don't allow the stddev value used in the
weight calculation to be smaller than the precision and also assign
weight of 1 to all samples which have distance < minimum + precision.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
cc507bffae conf: abort when include directive fails
When parsing the include directive, call glob() with the GLOB_ERR and
GLOB_NOMAGIC flags, and abort with an error message when matching of the
pattern failed with other error than GLOB_NOMATCH.

This restores the original behavior of the directive when it didn't
allow patterns, but it will still not fail with patterns not matching
any files in an existing directory.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
0dbfe020ad refclock: set default precision to precision of system clock 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
018a1c42b0 ntp: suggest clients to increase their polling interval
When the poll value in a client request is smaller than the server's NTP
rate limiting interval, set poll in the response to the rate limiting
interval to suggest the client to increase its polling interval.

This follows ntpd as a server. No current client implementation seems to
be increasing its interval by the poll, but it may change in the future.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
c5735ebfe9 util: add support for getrandom()
Add support for the Linux getrandom() system call, which is available
in glibc since 2.25.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
db93180ce1 ntp: apply HW TX/RX compensation to system time
Apply the compensation to the cooked local time instead of HW time. This
might make a difference when the HW clock has a large frequency error.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
39da10d939 doc: update description of hwtimestamp directive 2017-06-27 15:29:01 +02:00
Miroslav Lichvar
f2da253bc3 ntp: add option to select HW RX timestamping filter
Add an rxfilter option to the hwtimestamp directive to select which
received packets should be timestamped. It can be set to "none", "ntp",
or "all". The default value is ntp, which falls back to all when ntp is
not supported.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
934d4047f1 ntp: add support for new Linux timestamping options
New timestamping options may be available in kernel 4.13. They can be
used to get the index of the interface which timestamped incoming packet
together with its length at layer 2, enable simultaneous SW and HW TX
timestamping, and enable a new RX filter for NTP packets.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
b799cfd1c4 ntp: always try to enable SW timestamping on Linux
Request SW timestamps with SCM_TIMESTAMPING even if HW timestamping is
enabled. This replaces SCM_TIMESTAMP(NS) for RX and enables TX SW
timestamping on interfaces that don't support HW timestamping (or don't
have it enabled) if another interface has HW timestamping enabled.
2017-06-27 15:29:01 +02:00
Miroslav Lichvar
b712c100d7 main: close logs as last thing before exit
This should prevent losing messages from other finalisation code.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
c049bce007 client: try to connect to all addresses before giving up
Don't give up when one of the addresses/hostnames specified by -h fails
to resolve in DNS_Name2IPAddress(), e.g. with the default setting try to
connect to ::1 even when 127.0.0.1 failed due to the -6 option.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
46fad717e5 client: use getopt() for command line parsing 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
ae0c3bbbe8 main: use getopt() for command line parsing
This allows multiple options to be specified together and also may
options follow configuration directives on systems where getopt()
permutates the arguments.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
f95d57e0d9 doc: fix typo in chronyd man page 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
a1cbd4eb82 main: add option to specify log file
Add -l option to log to a file instead of syslog or terminal.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
6cbeb107db logging: allow logging to file instead of syslog 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
3a5566c6c3 main: use LOG_FATAL to print error when UID is not zero 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
73c548ad01 sourcestats: handle negative elapsed time in SST_GetSelectionData()
Source selection uses the last event time as current time. If it was
called from a refclock which generates a sample in its poll function
(e.g. PHC), the sample time may be later than the event time. This
gives a negative elapsed time in SST_GetSelectionData() and possibly
also a negative root distance, which causes the source to be rejected as
a falseticker.

Use absolute value of the difference in order to always get a positive
root distance.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
82203e12c8 doc: update refclock documentation 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
1ca099473f refclock: add option to filter wrong pulse edges
Add width option to the refclock directive to set expected width of
pulses in a PPS signal. The width adds a limit for the maximum offset
and root distance in order to reject PPS samples from wrong events, e.g.
PHCs which cannot be configured to timestamp only rising of falling
edges.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
eceb8d9937 refclock_phc: add support for timestamping of external PPS
Add extpps driver option to the PHC refclock to enable external
timestamping of PPS signal and also options to configure the channel and
pin index. In this mode, the driver polling function accumulates samples
for hwclock, which is used to convert received timestamping events to
local time.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
4ba92bb6d6 sys_linux: add support for external PHC timestamping 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
f31f68ae8e refclock: add option to treat non-PPS refclocks as PPS
Add pps option to the refclock directive to force chronyd to treat any
refclock as a PPS refclock. This is intended for refclocks that may
provide time off by a whole number of seconds due to missing or wrong
TAI/GPS->UTC conversion.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
cff15f91d4 refclock: allow all drivers to provide PPS samples 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
6b74917954 refclock: allow drivers to provide cooked PPS samples
Split RCL_AddPulse() in order to provide a new function for refclock
drivers which can make PPS samples without having raw system time, e.g.
from PHC timestamps.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
1bf2384a1f refclock: don't require raw time in valid_sample_time()
This makes the check a bit more expensive, but it will be needed to
allow refclocks that don't have raw system time.
2017-05-26 13:33:53 +02:00
Miroslav Lichvar
54a12779e2 ntp: include local error in hwclock samples 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
e8b06fef9f ntp: remove unnecessary include 2017-05-26 13:33:53 +02:00
Miroslav Lichvar
653d70ec4e sys_linux: allow sysinfo in seccomp filter
It may be used by glob() in latest glibc.
2017-04-19 14:38:51 +02:00
Miroslav Lichvar
abb09418b1 sys_linux: don't drop PHC samples with zero delay
When processing data from the PTP_SYS_OFFSET ioctl, the sample is
dropped when an interval between two consecutive readings of the system
clock is negative or zero, assuming the clock has been stepped between
the two readings.

With a real PHC the interval is normally expected to be at least a
microsecond, but with a virtual PHC and a low-resolution system clock
it's possible to get two readings with the same system time. Modify the
check to drop only samples with a negative delay.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
c103bebd9f configure: check for clang
Try clang as the C compiler before cc and use the same -W* CFLAGS as
with gcc.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
935d855b47 util: indicate truncated Unix socket path in UTI_SockaddrToString()
Specify the maximum length of the path in the snprintf() format to avoid
a new gcc warning (-Wformat-truncation). If the path doesn't fit in the
buffer, indicate with the '>' symbol that it was truncated. The function
is used only for debug messages.
2017-04-19 13:03:10 +02:00
Miroslav Lichvar
f8f9100a0d makefile: run tests in multiple iterations on check
Use the new options of the run script in the check target to make it
reliable for automatic testing without using a fixed random seed and add
a new quickcheck target for the original check using just one iteration.
2017-03-31 14:53:27 +02:00
Miroslav Lichvar
6de7b98e76 test: improve run script
Add options to allow running the tests in multiple iterations while
allowing a small number of failures per test. Some tests are expected to
fail occasionally as they are basically statistical tests. Improving
their reliability is possible, but it's always a compromise between
sensitivity, reliability, and execution time.
2017-03-31 14:53:27 +02:00
Miroslav Lichvar
c390351c65 test: make 118-maxdelay more reliable 2017-03-31 14:53:27 +02:00
Miroslav Lichvar
768bce799b sys_linux: allow getpid in seccomp filter
It seems to be used by syslog() in latest glibc.
2017-03-13 14:42:44 +01:00
Miroslav Lichvar
d3a30142e5 test: fix DEBUG_LOG use in unit tests
This was missing in commit f282856c72.
2017-03-13 12:04:26 +01:00
Chris Perl
3a635fc51f sourcestats: reorder arguments to DEBUG_LOG in SST_IsGoodSample
The delay_increase and allowed_increase variables are backwards with
respect to the ordering of the words in the message.
2017-03-10 16:55:22 +01:00
Miroslav Lichvar
10078566da test: make 117-fallbackdrift more reliable 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
c44346096c sys: add null driver
Add a new clock driver that doesn't actually try to adjust the clock.
It allows chronyd to run without the capability to adjust/set the system
clock, e.g. in some containers. It can be enabled by the -x option.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
0ff449e6a6 local: improve log message for failed clock step 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
f3a16383b9 main: dump history by default
Always write the measurement history on exit when the dump directory is
specified and silently ignore the dumponexit directive. There doesn't
seem to be a good use case for dumpdir and -r without dumponexit as the
history would be invalidated by adjustments of the clock that happened
between the dump command and chronyd exit.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
539ef3f770 main: rewrite some error messages 2017-03-10 16:51:03 +01:00
Miroslav Lichvar
f282856c72 logging: remove facility parameter
It was never used for anything and messages in debug output already
include filenames, which can be easily grepped if there is a need
to see log messages only from a particular file.
2017-03-10 16:51:03 +01:00
Miroslav Lichvar
6db8ec1ba2 privops: separate res_init() call
Move the res_init() call from do_name_to_ipaddress() into a separate
privops operation. Use it in ntp_sources and avoid unnecessary
res_init() calls in the main thread.
2017-03-10 16:51:02 +01:00
Miroslav Lichvar
5187c08c90 doc: update NEWS 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
c8076ac10d makefile: fix distclean target to not print errors 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
362d155558 examples: improve configuration examples 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
7b7eb0a6e5 examples: improve systemd unit files
Add the PrivateTmp, ProtectHome, and ProtectSystem directives to better
secure the system from chronyd. It's taken from the Debian chrony
package.
2017-01-31 11:22:11 +01:00
Miroslav Lichvar
d96f49f67d test: add keys unit test 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
43ba5d2126 doc: document rekey in chronyc man page
For some reason this useful command was never documented.
2017-01-31 11:22:11 +01:00
Miroslav Lichvar
48f7598fed client: add rekey to help text 2017-01-31 11:22:11 +01:00
Miroslav Lichvar
510b22e96b util: fix more coverity warnings
Coverity doesn't seem to like the new field in the IPAddr struct (used
as explicit padding of the structure) to be left uninitialized, even
though it's never used for anything and is cleared by memset() in
UTI_IPHostToNetwork() before leaving the process.
2017-01-31 11:22:10 +01:00
Miroslav Lichvar
0a0aff14d8 conf: add rawmeasurements log option
While the measurements log can be useful for debugging problems in NTP
configuration (e.g. authentication failures with symmetric keys), it
seems most users are interested only in valid measurements (e.g. for
producing graphs) and don't expect/handle entries where some of the RFC
5905 tests 1-7 failed. Modify the measurements log option to log only
valid measurements, and for debugging purposes add a new rawmeasurements
option.
2017-01-31 11:22:10 +01:00
Miroslav Lichvar
e225ac68bc test: update 110-chronyc 2017-01-27 11:54:12 +01:00
Miroslav Lichvar
58060c40a5 doc: improve FAQ 2017-01-27 11:45:50 +01:00
Miroslav Lichvar
2ac1b3d5c4 client: print tracking delay/dispersion in nanosecond resolution 2017-01-27 11:35:38 +01:00
Miroslav Lichvar
c174566982 ntp: check supported flags before enabling HW timestamping 2017-01-27 11:35:38 +01:00
Miroslav Lichvar
60fca19d40 ntp: log info message when HW timestamping is enabled 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
8bcb15b02f doc: improve description of some server options 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
65c2cebcd5 reference: report zero root dispersion with local reference
The server's precision is supposed to be included in client's
dispersion. Don't include it in the server's dispersion.
2017-01-27 10:55:28 +01:00
Miroslav Lichvar
2a51b45a43 test: fix memory leaks in unit tests 2017-01-27 10:55:28 +01:00
Miroslav Lichvar
5ac791665e doc: update NEWS 2017-01-24 15:03:24 +01:00
Miroslav Lichvar
a4e3f83611 update copyright years 2017-01-24 15:01:38 +01:00
Miroslav Lichvar
8a837f9c2b test: extend 119-smoothtime 2017-01-23 16:17:39 +01:00
Miroslav Lichvar
da2d33e9a8 ntp: fix time smoothing in interleaved mode
When the server's transmit timestamp was updated with a kernel/HW
timestamp, it didn't include the time smoothing offset. If the offset
was larger than one second, the update failed and clients using the
interleaved mode received less accurate timestamps. If the update
succeeded, the clients received timestamps that were not adjusted for
the time smoothing offset, which added an error of up to 0.5s/1s to
their measured offset/delay.

Fix the update to include the smoothing offset in the new timestamp.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4b98dadae9 ntp: simplify UTI_Ntp64ToTimespec() callers
Since UTI_Ntp64ToTimespec() was modified to handle zero timestamps, some
of its callers don't need to do that anymore.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
86acea5c46 ntp: add interface index to NTP_Local_Address
This will allow us to get the interface index when sending responses to
clients.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
a60fc73e7b refclock_phc: add nocrossts option 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
50f99ec5f4 conf: add nocrossts option to hwtimestamp directive
This option disables the use of the PTP_SYS_OFFSET_PRECISE ioctl.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
31b6a14444 sys_linux: add support for PTP_SYS_OFFSET_PRECISE
This is for hardware that can precisely cross timestamp the PHC with the
system clock.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9df4d36157 refclock_phc: use sys_linux code for reading PHC
This drops support for non-ioctl reading of PHC.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b70f0b674f ntp: move PHC-specific code to sys_linux
This will allow sharing of the code with the PHC refclock driver.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
510784077f conf: add minpoll option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
9800e397fb hwclock: make minimum sampling separation configurable 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
1436d9961f conf: add precision option to hwtimestamp directive 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
98f5d05925 ntp: include precision of PHC readings in their selection
Include a fixed non-zero precision (100 nanosecond) in the selection of
PHC readings.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
7a937c7652 conf: return hwtimestamp data in struct 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
b198d76676 ntp: include precision in maxdelay test 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
97d4203354 ntp: adapt sampling separation for short polling intervals 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
beaaaad162 ntp: allow sub-second polling intervals
Change the minimum minpoll to -4, but keep the minimum maxpoll at 0 in
order to not make it too easy to flood distant servers.
2017-01-23 15:58:55 +01:00
Miroslav Lichvar
4e78975909 ntp: use current poll when backing off on KoD RATE 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
99147ed8f2 ntp: rename maxdelay constants 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
dec0d3bfc2 ntp: reset ntpdata report on address change 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
cd84c99e70 examples: improve chronyd.service 2017-01-23 15:58:55 +01:00
Miroslav Lichvar
d5c507975c doc: update README 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
b4235abd36 update copyright years 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
1966085a97 test: add ntp_core unit test 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
e31e7af48f test: make 119-smoothtime more reliable 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
adb9123fc3 test: extend util unit test 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
b0f7efd59e util: handle zero in conversion of NTP timestamps
Handle zero NTP timestamp in UTI_Ntp64ToTimespec() as a special value to
make it symmetric with UTI_TimespecToNtp64(). This is needed since
commit d75f6830f1, in which a timestamp is
converted back and forth without checking for zero.

It also makes zero NTP timestamps more apparent in debug output.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
e28dfada8c rtc: check for backward RTC steps
When accumulating a new sample, check if the new RTC time is newer the
last sample time. If it is not, discard all previous samples, assuming
something has stepped the RTC, or it's a broken RTC/driver.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
ac0b28cce6 sourcestats: align sample time used for source report
This reduces leak of sample times (and receive timestamps which are
related to sample times), which could be useful in off-path attacks on
unauthenticated symmetric interleaved mode.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
48b16ae66c local: add assertion for precision 2017-01-12 16:34:28 +01:00
Miroslav Lichvar
061579ec28 ntp: don't send packets with RX equal to TX
Before sending an NTP packet, check whether the TX timestamp is not
equal to the RX timestamp. If it is, generate a new TX timestamp and try
again. This is extremely unlikely to happen in normal operation, but it
is needed for reliable detection of the interleaved mode.
2017-01-12 16:34:28 +01:00
Miroslav Lichvar
f2f834e7e7 ntp: limit maxdelay parameters 2017-01-12 16:34:27 +01:00
Miroslav Lichvar
a7802e9a76 fix some coverity warnings 2017-01-12 16:34:27 +01:00
Miroslav Lichvar
8f7ab95ff0 doc: update NEWS 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
042c670747 doc: improve chrony.conf man page 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
cacbe9976f ntp: add options for compensating HW timestamping errors 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
8efec1d640 ntp: add sanity check for HW timestamps
Accept HW timestamp only if it doesn't differ from the kernel/daemon
timestamp by more than one second.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
c44d282f0b ntp: ignore zero HW timestamps
Apparently, zero HW timestamps are possible with buggy drivers/HW.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
4432f29bd2 sources: try to replace jittery sources
Similarly to falsetickers, distant, and unreachable sources, try to
replace sources that have jitter larger than maxjitter.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
5fee3ed5e9 client: print refid also as string in ntpdata output 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
b76ea64263 ntp: log warning when KoD RATE is received in non-burst mode 2017-01-06 13:12:19 +01:00
Miroslav Lichvar
ed904f08a4 hwclock: return timestamp error
For now, when converting a raw timestamp, return error of the last
sample as the maximum error of the timestamp. This is needed to include
the PHC reading delay in the NTP dispersion.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
96cc80ffc8 ntp: improve dispersion calculation
Instead of adding precision (sum of the local and remote precision) to
the TX and RX timestamp error, include only the maximum.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
ab99373cfc conf: change default rate limiting parameters
Change the default NTP rate limiting leak to 2 (25%). Change the default
command rate limiting interval to -4 (16 packets per second) and burst
to 8, so the interval is the only difference between NTP and command
rate limiting defaults.
2017-01-06 13:12:19 +01:00
Miroslav Lichvar
dbfb49384b clientlog: disable NTP response rate limiting by default
This reverts commit 50022e9286.

Testing showed that ntpd as an NTP client performs poorly when it's
getting only 25% of responses. At least for now, disable rate limiting
by default again.
2017-01-06 13:12:18 +01:00
Miroslav Lichvar
14bb9f29a3 ntp: calculate delay relative to local frequency
This should be more accurate as local frequency is usually
combined from multiple sources. This is a partial revert of commit
23a4e8b38d.
2017-01-06 13:12:18 +01:00
Miroslav Lichvar
16519ee2cc doc: update NEWS 2016-12-15 13:47:41 +01:00
Miroslav Lichvar
50022e9286 clientlog: enable NTP response rate limiting by default
Change the default interval of both NTP and command rate limiting to -10
(1024 packets per second) and the burst to 16. The default NTP leak is 2
(rate limiting is enabled by default) and the default command leak is 0
(rate limiting is disabled by default).
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
5059019535 clientlog: randomize alignment of log timestamps 2016-12-15 13:47:41 +01:00
Miroslav Lichvar
c6a38f5069 clientlog: allow very short rate limiting intervals
Support negative token shift to allow coarse rate limiting with
intervals down to -19.
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
11ed197663 configure: don't use recvmmsg() on FreeBSD
Don't try recvmmsg() on FreeBSD, at least for now. It is broken on
FreeBSD 11.0 and it's just a wrapper around recvmsg().
2016-12-15 13:47:41 +01:00
Miroslav Lichvar
5634e6b963 doc: improve hwtimestamp description 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
db312a5ff6 ntp: allow wildcard in hwtimestamp directive
If "*" was specified, use getifaddrs() to get a list of all interfaces,
and try to enable HW timestamping on all of them.
2016-12-14 16:19:35 +01:00
Miroslav Lichvar
88c31b3785 client: improve ntpdata output 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
967f3e4f77 client: don't require address in ntpdata command
If no address is specified, use the SOURCE_DATA command to get addresses
of NTP sources, and request NTP_DATA for all of them.
2016-12-14 16:19:35 +01:00
Miroslav Lichvar
2e311d1766 sourcestats: add upper bound for skew 2016-12-14 16:19:35 +01:00
Miroslav Lichvar
11f7cc0507 examples: avoid Unix domain socket in chrony-wait service
Use the -h option to force chronyc to use internet socket instead of
Unix domain as the access to the socket may be blocked by SELinux and
trying to open it generates SELinux warnings.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
a4f28892a5 cmdmon: update protocol changelog 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
5bc53741be sourcestats: add lower bound for std dev used for weighting 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
95a4f33265 sourcestats: save asymmetry run in dump files
This allows the asymmetry correction to be applied right after restart.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
fac1093ebf cmdmon: add reserved fields to ntpdata reply
This might be useful if ntpdata is changed to not require authorization
and new fields need to be added without breaking compatibility.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
1b1384ccaa nameserv: set CLOEXEC flag on pipe file descriptors 2016-12-13 12:57:25 +01:00
Miroslav Lichvar
0c9a19ded5 stubs: rework emulation of asynchronous resolver to use pipes
With a larger number of configured servers, the handler of the emulated
resolver repeatedly scheduled timeout of zero, which triggered the
infinite loop detection in the scheduler and caused abort. This bug was
introduced in commit 967e358dbc.

Rework the code to use pipes instead of timeouts to avoid this problem.
2016-12-13 12:57:25 +01:00
Miroslav Lichvar
b7bd7469b7 ntp: disable maxdelayratio in interleaved/symmetric mode
It's too unreliable and the maxdelaydevratio test should work better
anyway.
2016-12-13 12:57:24 +01:00
Miroslav Lichvar
9568ff3f06 doc: update NEWS 2016-12-09 09:04:25 +01:00
Miroslav Lichvar
742ddcce11 doc: update README 2016-12-08 16:26:34 +01:00
Miroslav Lichvar
e72cc9e3da test: update 119-smoothtime 2016-12-08 16:25:46 +01:00
Lonnie Abelbeck
3156e5a293 client: add tab-completion with libedit/readline 2016-12-08 15:32:51 +01:00
Miroslav Lichvar
9a901e1cb0 refclock: make maximum lock age configurable
The maxlockage option specifies in number of pulses how old can be
samples from the refclock specified by the lock option to be paired with
the pulses. Increasing this value is useful when the samples are
produced at a lower rate than the pulses.
2016-12-08 14:47:38 +01:00
Miroslav Lichvar
8c11044ee2 refclock: slew last sample even after it was used
It may be needed by locked PPS refclocks.
2016-12-08 14:47:38 +01:00
Miroslav Lichvar
a75d2db75b test: add scan-build compilation test 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
6aac72fd80 configure: use common CPPFLAGS for all objects 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
b692cb720c configure: fix help text 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
25102489f5 ntp: fix clang warning 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
a2d2cad384 hwclock: fix check of sample separation 2016-12-08 14:47:38 +01:00
Miroslav Lichvar
859e0c2323 ntp: add TX error to dispersion 2016-12-08 14:47:36 +01:00
Miroslav Lichvar
e62a39cafe ntp: fix RX error added to dispersion in interleaved mode 2016-12-08 14:47:33 +01:00
Miroslav Lichvar
8bbb8fa062 sources: add configurable limit for jitter
The maxjitter directive sets the maximum allowed jitter of the sources
to not be rejected by the source selection algorithm. This prevents
synchronisation with sources that have a small root distance, but their
time is too variable. By default, the maximum jitter is 1 second.
2016-12-08 14:20:00 +01:00
Miroslav Lichvar
68039e0d14 sourcestats: save variance as standard deviation
This reduces the number of sqrt() calls.
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
65fd30a547 cmdmon: allow all parameters to be set for new sources
Add missing fields to the REQ_NTP_Source structure and add new versions
of the ADD_SERVER/ADD_PEER commands.
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
23a4e8b38d ntp: rework calculation and testing of peer delay
Instead of a worst-case delay use a mean value and relate it to the
source's time. This makes it more stable in the interleaved and
symmetric modes, which should improve the weighting and asymmetry
correction. Modify the test A and B to work with a minimum estimated
delay (delay - dispersion).
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
979b53866d client: print addresses with refids in ntpdata report 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
46061d8eec client: fix truncation of long hostnames 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
946ee8f611 client: fix format specifier for poll in ntpdata report 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
7f757f09ce client: fix add command
The default version changed to 0 (autoselect).
2016-12-06 16:56:38 +01:00
Miroslav Lichvar
9ba8a33966 sys_linux: allow openat in seccomp filter 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
492940568d main: add -t option to usage text 2016-12-06 16:56:38 +01:00
Miroslav Lichvar
b95c2a3f78 configure: rename SOCKDIR to RUNDIR 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
53b661b59d regress: remove unused struct declaration 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
a049c9e0f8 conf: increase default minsamples and polltarget
Change default minsamples to 6 and polltarget to 8. This should improve
stability with extremely small jitters (e.g. HW timestamping) and not
decrease time accuracy at minimum polling interval too much.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
3513484852 main: add -t option to chronyd
This option sets a timeout (in seconds) after which chronyd will exit.
If the clock is not synchronised, it will exit with a non-zero status.
This is useful with the -q or -Q option to shorten the maximum time
waiting for measurements, or with the -r option to limit the time when
chronyd is running, but still allow it to adjust the frequency of the
system clock.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
2d67871bbf ntp: don't make client log entries for broadcast TX 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
e6e9a472db ntp: avoid truncation of NTPv4 MACs by default
If the MAC in NTPv4 requests would be truncated, use version 3 by
default to avoid the truncation. This is necessary for compatibility
with older chronyd servers, which do not respond to messages with
truncated MACs.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
1d5d768545 test: extend 105-ntpauth 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
6c8588c13c ntp: truncate MACs in NTPv4 packets
When sending an NTPv4 packet, truncate long MAC to 192 bits to follow
RFC 7822.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
89b127bf6c ntp: accept NTPv4 packets with truncated MACs
In order to allow deterministic parsing of NTPv4 extension fields, the
MAC must not be longer than 192 bits (RFC 7822). One way to get around
this limitation when using symmetric keys which produce longer MACs is
to truncate them to 192 bits (32-bit key ID and 160-bit hash).

Modify the code to accept NTPv4 packets with MACs truncated to 192
bits, but still allow long MACs in NTPv4 packets to not break
compatibility with older chrony clients.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
38c4a7ff97 keys: add support for checking truncated MACs 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
2f5b4aea91 util: move authentication and password decoding functions to keys
This doesn't need to be included in chronyc.
2016-12-02 14:53:03 +01:00
Miroslav Lichvar
4fc6a1b424 doc: update FAQ 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
6b3800cc94 doc: update man pages 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
633a007b7b doc: update README 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
756c2e9afb ntp: fix length modifier of refid in measurements log 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
27ea58d5fd client: zero pad reference ID 2016-12-02 14:53:03 +01:00
Miroslav Lichvar
64f9205189 client: add ntpdata command 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
535ca64bba cmdmon: add ntpdata command 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
7255f9ef74 client: fix format specifiers in sourcestats report 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
cdb0b6124f client: add new format specifiers to print_report() 2016-11-25 17:33:43 +01:00
Miroslav Lichvar
5fb1107cc7 client: print reference ID in hexadecimal
This is an incompatible change in the output of the tracking command,
which may break some scripts, but it's necessary to avoid confusion with
IPv4 addresses when synchronised to an IPv6 server or reference clock.
2016-11-25 17:33:43 +01:00
Miroslav Lichvar
1045adaa88 sources: give access to sourcestats instance
Give access to the sourcestats instance and remove all functions that
just translated to SST calls.
2016-11-25 17:33:43 +01:00
Miroslav Lichvar
ed286f3617 ntp: add new debug message 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
9f9dd7948b ntp: fix logging of RX timestamp source in interleaved mode 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
9c760de676 ntp: don't send presend packets in burst mode 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
90229984cf ntp: allow presend of zero
Don't use zero as a special value for disabled and change the default
presend to a value larger than any valid poll.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
2b3d64c31d ntp: send two presend packets in interleaved mode
In a burst of three requests (two presend + one normal) the server can
detect the client is using the interleaved mode and save the transmit
timestamp of the second response for the third response. This shortens
the interval in which the server has to keep the state.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
d23c647e34 ntp: shorten presend delay to 2 seconds 2016-11-25 17:33:42 +01:00
Miroslav Lichvar
2408bbcd77 ntp: process presend responses
Rework the code to make a real request for presend and process the
response, but don't accumulate the sample. This allows presend to work
in the interleaved client mode.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
d75f6830f1 reference: randomize reference time
In unauthenticated interleaved symmetric NTP mode we should be now
careful with the reference timestamp as it may be useful with the peer
delay for estimating the local receive timestamp and increasing the
chance of spoofing a valid response from the peer.

When updating the reference time, add a random error of up to one second
to make it less sensitive when disclosed to NTP and cmdmon clients.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
4d7eb2f7a6 ntp: don't reset polling interval when switching to/from online
This allows chronyd to ramp up the polling interval even when the source
is frequently switched between the online and offline modes.
2016-11-25 17:33:42 +01:00
Miroslav Lichvar
3a67dedad6 ntp: fix calculation of PHC sample time 2016-11-23 10:08:36 +01:00
Miroslav Lichvar
518837e17a sys_linux: allow ioctls used with HW timestamping in seccomp filter 2016-11-23 09:24:05 +01:00
Miroslav Lichvar
c7e778757a ntp: transpose HW RX timestamps
We need to transpose HW RX timestamps as HW timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
timestamps. Without raw sockets we don't know the length of the packet
at layer 2, so we make an assumption that UDP data start at the same
position as in the last transmitted packet which had a HW TX timestamp.
2016-11-22 16:15:35 +01:00
Miroslav Lichvar
c45be946ce Merge branch '2.4-stable' into HEAD 2016-11-22 16:06:05 +01:00
Miroslav Lichvar
258bcc21b8 refclock: don't compare sample time with samples from previous poll
This is an improvement of commit 8f85291d23.
2016-11-22 15:58:02 +01:00
Miroslav Lichvar
875b0e262c ntp: add debug message for truncated control messages 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
8823e2b064 ntp: ignore truncated messages
Don't waste time with processing messages that don't fit in the receive
buffer as they most likely wouldn't pass the format check due to an
invalid length of an extension field.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
5b2caf48dc hwclock: fix order of samples
In order to trim oldest samples in the regression function, they need to
be sorted in the data arrays from the oldest to newest.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
7ec048ce7f ntp: detect unexpected TX updates of unknown sources 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
cfb3c3ba44 ntp: improve replay protection in symmetric mode
Always allow update from the first valid response, even if its transmit
timestamp is not newer than the currently saved timestamp. This shoud
provide a temporary protection in the case where the attacker does have
an authenticated packet from future, but the peers are using the same
polling interval and the protocol is already synchronised. This could be
also useful in the case where the attacker cannot observe the traffic
and authentication is disabled.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
4b0ef09221 sched: add more random bits to timeout scheduling
Extend the random value which is included in the calculation of the
delay from 16 to 32 bits. This makes scheduling of NTP transmissions
random to one microsecond for polling intervals up to 17.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
74f581e7ab client: randomize sequence number in requests
Don't rely on random source port of a connected socket alone as a
protection against spoofed packets in chronyc. Generate a fully random
32-bit sequence number for each request and modify the code to not send
a new request until the timeout expires or a valid response is received.
For a monitoring protocol this should be more than good enough.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
07aa54b183 client: fix attempt number in requests to be in network order 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
00da177e51 report: remove unused definition 2016-11-15 14:55:25 +01:00
Miroslav Lichvar
6e9bfac07d sources: add new status for sources that overlap trusted sources
Sources that overlap trusted sources should be displayed in the chronyc
sources report with the '-' symbol and they shouldn't trigger a
replacement.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
06f93e7bf0 sources: don't log warning when opening dump file fails
Instead of complaining when the file doesn't exist, which is common when
using pool servers, log an informational message when the file is
loaded.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
d84a706c08 conf: create socket directory before logdir and dumpdir
This allows sharing of the same directory for sockets, logs and dumps as
the socket directory needs to be created first (with mode 0770) in order
to pass the check of the permissions.
2016-11-15 14:55:25 +01:00
Miroslav Lichvar
ea58a1e72c ntp: print offset and delay in debug messages in nanosecond resolution 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
5c691a5460 ntp: fix remote poll in measurements log
Write the poll value from the received packet instead of the saved
value, which doesn't have to be always updated.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
2c877fa149 ntp: add new fields to measurements log
Include reference ID, NTP mode and source of the local transmit and
receive timestamp in the measurements log.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
33053a5e14 ntp: add partial protection against replay attacks on symmetric mode
A recently published paper [1] (section VIII) describes a DoS attack
on symmetric associations authenticated with a symmetric key where the
attacker can only observe and replay packets. Although the attacker
cannot prevent packets from reaching the other peer (not even by
flooding the network for example), s/he has the same power as a MitM
attacker.

As the authors explain, this is a fundamental flaw of the protocol,
which cannot be fixed in the general case. However, we can at least try
to protect associations in a case where the peers use the same polling
interval (i.e. for each request is expected one response) and all peers
that share the symmetric key never start with clocks in future or very
distant past (i.e. the attacker does not have any packets from future
that could be replayed).

Require that updates of the NTP state between requests have increasing
transmit timestamp and when a packet that passed all NTP tests to be
considered a valid response was received, don't allow any more updates
of the state from packets that don't pass the tests. This should ensure
the last update of the state is from the first time the last real
response was received and still allow the protocol to recover in case
one of the peers steps its clock back or the attacker does have a packet
from future and the attack stops.

[1] Aanchal Malhotra, Matthew Van Gundy, Mayank Varia, Haydn Kennedy,
    Jonathan Gardner, and Sharon Goldberg. The Security of NTP's
    Datagram Protocol. https://eprint.iacr.org/2016/1006
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
8662652192 ntp: disable presend in symmetric and interleaved modes
The presend packet can't be used in symmetric and interleaved modes as
it breaks the protocol with unexpected packets.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
227c7e60a4 test: add util unit test 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
6e9c04896b util: add functions for zeroing and comparing NTP timestamps 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
0e273939d2 ntp: fix poll value in broadcast mode packets
Set poll in broadcast mode packets to the rounded log2 value of the
actual interval instead of a hardcoded value.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14647032b2 doc: update chrony.conf man page for recent changes 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14a1059e43 ntp: add support for HW timestamping on Linux
Add a new directive to specify interfaces which should be used for HW
timestamping. Extend the Linux ntp_io initialization to enable HW
timestamping, configure the RX filter using the SIOCSHWTSTAMP ioctl,
open their PHC devices, and track them as hwclock instances. When
messages with HW timestamps are received, use the PTP_SYS_OFFSET ioctl
to make PHC samples for hwclock.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
4449259d88 ntp: read interface index from control messages 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
01e5ea7d31 test: add 122-xleave 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
94522bfed1 test: add hwclock unit test 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
9bdd35c9fa hwclock: add support for tracking hardware clocks
Add a general support for tracking independent hardware clocks like PTP
hardware clocks (PHC) or real-time clocks (RTC).
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
d366530699 clientlog: move status check to get_record() 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
96d652e5bd ntp: add support for interleaved client/server mode
Adapt the interleaved symmetric mode for client/server associations.
On server, save the state needed for detection and responding in the
interleaved mode in the client log. On client, enable the interleaved
mode when the server is specified with the xleave option. Always accept
responses in basic mode to allow synchronization with servers that
don't support the interleaved mode, have too many clients, or have
multiple clients behing the same IP address. This is also necessary to
prevent DoS attacks on the client by overwriting or flushing the server
state. Protect the client's state variables against replay attacks as
the timestamps are now needed when processing the subsequent packet.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
bd736f9234 ntp: check also NTP receive timestamp when updating TX timestamp 2016-11-10 15:26:56 +01:00
Miroslav Lichvar
90b25f5b83 ntp: add support for interleaved symmetric mode
Add xleave option to the peer directive to enable an interleaved mode
compatible with ntpd. This allows peers to exchange transmit timestamps
captured after the actual transmission and significantly improve
the accuracy of the measurements.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
997406fe47 ntp: add support for software timestamping on Linux
Enable SCM_TIMESTAMPING control messages and the socket's error queue in
order to receive our transmitted packets with a more accurate transmit
timestamp. Add a new file for Linux-specific NTP I/O and implement
processing of these messages there.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
14c8f07629 ntp: save source of local timestamps
Introduce a new structure for local timestamps that will hold the
timestamp with its estimated error and also its source (daemon, kernel
or HW). While at it, reorder parameters of the functions that accept the
timestamps.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
8f6a1b5318 ntp: add support for processing of transmitted packets
Add new functions for processing of packets after they are actually
sent by the kernel or HW in order to get a more accurate transmit
timestamp. Rename old functions for processing of received packets and
their parameters to make the naming more consistent.
2016-11-10 15:26:56 +01:00
Miroslav Lichvar
a8c6bea2d5 sys_linux: add function for checking kernel version 2016-11-10 15:26:55 +01:00
Miroslav Lichvar
19fde8f49c refclock: fix check for old samples
The fix in commit 8f85291d23 was
incorrect.
2016-10-07 11:03:59 +02:00
Miroslav Lichvar
8f85291d23 refclock: require new samples to have newer timestamp
If all or most SHM/SOCK samples collected in a polling interval had the
same local timestamp, the dispersion could end up as nan, which could
trigger an assert failure later in the code.

Before accumulating a refclock sample, check if the timestamp is newer
than the previous one.
2016-10-06 16:09:24 +02:00
Miroslav Lichvar
9c48166e90 ntp: inline send_packet()
Also, reuse existing function for checking server sockets.
2016-09-26 12:40:44 +02:00
Miroslav Lichvar
b536296c05 ntp: use ipi_addr from struct in_pktinfo as local address
Use the ipi_addr field instead of ipi_spec_dst as the local address
after recvmsg() to be consistent with the processing of struct
in6_pktinfo. This may make a difference for messages from the error
queue.
2016-09-26 12:40:44 +02:00
Miroslav Lichvar
d36c522453 ntp: check for missing source address after recvmsg() 2016-09-26 12:40:44 +02:00
Miroslav Lichvar
2577e20f09 ntp: fix updating of transmit delay in symmetric mode
This was broken in commit cea21adbbb.
2016-09-26 12:40:43 +02:00
Miroslav Lichvar
c169ad3f58 sched: add support for handling exceptions on descriptors 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
411f4697ca sys_linux: allow getdents in seccomp filter
This is needed for glob(), which is used with the include and dumpdir
directives.
2016-09-26 12:40:43 +02:00
Miroslav Lichvar
6c5de8dcb0 refclock: use UTI_TimespecToString() in debug message 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
c8373f1649 util: add UTI_IsZeroTimespec() 2016-09-26 12:40:43 +02:00
Miroslav Lichvar
45f86122fa test: add smooth unit test 2016-09-12 13:13:30 +02:00
Miroslav Lichvar
c0a8afdb68 smooth: fix selection of 1st stage direction
When the smoothing process is updated with extremely small (e.g.
sub-nanosecond) values, both directions may give a negative length of
the 1st or 3rd stage due to numerical errors and the selection will fail
an in assertion. Rework the code to select the direction which gives a
smaller error.
2016-09-12 13:13:14 +02:00
Miroslav Lichvar
1afb285aad sched: initialize sub-second part of saved_tv in SCH_MainLoop()
This is needed since commit d0dfa1de9e to
avoid valgrind errors.
2016-09-12 12:49:18 +02:00
Miroslav Lichvar
c08e7e716d use correct facility in LOG messages 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
a06a5f1baa sources: remove dump files on start
When chronyd is starting, after the point where dump files are loaded,
remove all files in the dump directory that match the naming scheme used
for dump files. This prevents loading stale dump files that were not
saved in the latest run of chronyd.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
fb5d4f1da4 conf: disable dumpdir and logdir by default
Use empty string instead of "." (which is normally the root directory)
as the default value of dumpdir and logdir to indicate they are not
specified. Print warnings in syslog when trying to log or dump
measurements without dumpdir or logdir.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
d2e5b41369 client: flush stdout after printing prompt
Apparently fgets() doesn't flush stdout in some libc implementations.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
4b6b6e5cba client: remove out of date comment 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
27b4c396d0 client: fix printing of negative poll in sources report again
This was broken in commit 3f51805e62.
2016-09-07 11:16:01 +02:00
Miroslav Lichvar
41eb5b79cb client: check address in waitsync command 2016-09-07 11:16:01 +02:00
Miroslav Lichvar
23cf74d5c7 util: convert invalid addresses as IPADDR_UNSPEC 2016-09-07 11:15:57 +02:00
Miroslav Lichvar
1a038bfd50 test: add 011-asymjitter 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
dd02d67224 test: add support for testing with asymmetric jitter 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
648bf8bd3e test: extend 113-leapsecond 2016-09-06 15:48:59 +02:00
Miroslav Lichvar
82c4bfe5d2 sources: include trust option in leap second voting
When sources specified with the trust option pass the source selection,
ignore other sources in the vote of leap second status.
2016-09-06 15:48:59 +02:00
Miroslav Lichvar
98ba4ce4d5 configure: add options to set default pidfile and rtcdevice 2016-08-22 15:50:35 +02:00
Bryan Christianson
f63e414024 configure: add option --without-clock-gettime
clock_gettime() will be ignored even if it is present
2016-08-22 15:50:35 +02:00
Miroslav Lichvar
a8886603c2 ntp: fix processing of kernel timestamps on non-Linux systems
When the SO_TIMESTAMP socket option was enabled, the expected type of
control messages containing timestamps was SO_TIMESTAMP instead of
SCM_TIMESTAMP. This worked on Linux, where the two values are equal, but
not on the other supported systems. The timestamps were ignored and this
probably worsened the accuracy and stability of the synchronisation.
2016-08-22 15:50:35 +02:00
Miroslav Lichvar
4f10144b09 ntp: add corrected delay to debug message in process_receive() 2016-08-22 15:50:28 +02:00
Miroslav Lichvar
af664e6cec sourcestats: return success when loading dump file with no samples 2016-08-22 15:05:48 +02:00
Miroslav Lichvar
c30816eb65 sourcestats: remove warning messages from SST_LoadFromFile()
There is a generic error message in SRC_ReloadSources().
2016-08-22 15:05:48 +02:00
Miroslav Lichvar
b1accfd0ff sourcestats: make reading/writing dump files Y2106 ready
The sample times were written and read as unsigned long, which would
overflow in year 2016 on platforms that have 32-bit long.
2016-08-22 15:05:48 +02:00
Miroslav Lichvar
5c45e4ccb5 sources: improve naming of dump files
Include IP address instead of reference ID in the name of dump file
for NTP sources and for reference clocks format the reference ID as a
hexadecimal number instead of quad dotted notation.

Also, avoid dynamic memory allocation and improve warning messages.
2016-08-22 15:05:02 +02:00
Miroslav Lichvar
41cf867738 sourcestats: update regression after loading dump file
Call SST_DoNewRegression() immediately in SST_LoadFromFile instead of
relying on SRC_ReloadSources().
2016-08-19 18:25:02 +02:00
Bryan Christianson
02844e9b01 local: fix typo in strerror() call 2016-08-19 18:25:02 +02:00
Miroslav Lichvar
7a1ebc3467 ntp: add support for SO_TIMESTAMPNS socket option
Enable the SO_TIMESTAMPNS option to get kernel timestamps in nanosecond
resolution.
2016-08-19 13:55:20 +02:00
Miroslav Lichvar
8d89610ff6 local: add support for clock_gettime()
Use clock_gettime() to read the system clock in nanosecond resolution.
2016-08-19 13:54:58 +02:00
Miroslav Lichvar
cfe706f032 util: modify UTI_*ToDouble functions to return double directly 2016-08-19 12:53:09 +02:00
Miroslav Lichvar
99cc94529d util: rename functions dealing with integers in NTP format
This should prevent confusion with int32_t, int64_t and other types.
2016-08-19 12:53:09 +02:00
Miroslav Lichvar
d0dfa1de9e adopt struct timespec
Replace struct timeval with struct timespec as the main data type for
timestamps. This will allow the NTP code to work with timestamps in
nanosecond resolution.
2016-08-19 12:53:09 +02:00
Miroslav Lichvar
0899ab52dd util: return normalised timevals 2016-08-19 11:33:38 +02:00
Miroslav Lichvar
71e0ebcb6b ntp: don't send crypto-NAKs
Crypto-NAK is useful only with Autokey where it allows quick reset
of the association. There is no plan to support Autokey and NTS will
specify its own message for authentication errors.
2016-08-17 11:54:34 +02:00
Miroslav Lichvar
e488371b01 sourcestats: report asymmetry in statistics log 2016-08-11 11:24:48 +02:00
Miroslav Lichvar
39f34eb674 sourcestats: correct offsets with asymmetric network jitter
Estimate asymmetry of network jitter on the path to the source as a
slope of offset against network delay in multiple linear regression. If
the asymmetry is significant and its sign doesn't change frequently, the
measured offsets (which are used later to estimate the offset and
frequency of the clock) are corrected to correspond to the minimum
network delay. This can significantly improve the accuracy and stability
of the estimated offset and frequency.
2016-08-11 11:24:48 +02:00
Miroslav Lichvar
9d9d6c30cf sourcestats: add debug message for regression results 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
27d59e54cc regress: add linear regression with two independent variables 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
507a01ab17 sourcestats: extend array holding peer delays
Keep the same number of peer delays as offsets. This will be needed when
peer delay is included in offset correction.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
f7b8cd1a09 regress: save arrays of constants in single-precision
No need to waste space on double precision. Also, declare them as const.
2016-08-11 10:45:48 +02:00
Bryan Christianson
8bc48af630 rename 'Mac OS X' to 'macOS'
From the the release of macOS Sierra (Version 10.12) the Macintosh
operating system is called 'macOS'
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
b0838280a9 ntp: reset tentative flag only when sample was accumulated
When selecting sources from a pool, ignore responses which didn't
produce a new sample. Sources with acceptable delay (as configured by
the maxdelay* options) should be prefered.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
cea21adbbb ntp: close client sockets sooner with unsynchronised sources
When a valid packet is received from an unsynchronised source (i.e. only
a test of leap, stratum or root distance failed), there is no point in
waiting for another packet or the RX timeout, and the client socket can
be immediately closed.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
c619d555f0 test: add 010-multrecv 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
e306199588 ntp: add support for recvmmsg()
This is used to read multiple packets with one system call. It should
work on Linux and NetBSD.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
895c15d677 configure: include config.h in test code 2016-08-11 10:45:48 +02:00
Miroslav Lichvar
d18f9ca75a ntp: rework receiving messages
Allocate buffers for received messages on heap instead of stack and
prepare the code for receiving multiple messages at the same time.
2016-08-11 10:45:48 +02:00
Miroslav Lichvar
82e76c39d9 ntp: align buffers for control messages 2016-08-11 10:45:47 +02:00
Miroslav Lichvar
577aed4842 ntp: add support for MS-SNTP authentication in Samba
Add support for authenticating MS-SNTP responses in Samba (ntp_signd).
Supported is currently only the old MS-SNTP authenticator field. It's
disabled by default. It can be enabled with the --enable-ntp-signd
configure option and the ntpsigndsocket directive, which specifies the
location of the Samba ntp_signd socket.
2016-07-29 10:17:33 +02:00
Miroslav Lichvar
2a8ce63fc7 ntp: detect MS-SNTP packets
When a received packet fails to authenticate, check if the digest
contains zeroes and treat it as an MS-SNTP packet with authenticator or
extended authenticator field. For now, discard these packets, i.e. don't
respond with a crypto-NAK.
2016-07-29 10:17:33 +02:00
Miroslav Lichvar
61dd4e0ccb ntp: refactor selection of authentication mode
Replace the flag that enables authentication using a symmetric key with
an enum. Specify crypto-NAK as a special mode used for responses instead
of relying on zero key ID. Also, rework check_packet_auth() to always
save the mode and key ID.
2016-07-29 10:17:13 +02:00
Miroslav Lichvar
8220e51ae4 ntp: check for extension fields only in NTPv4 packets 2016-07-20 12:47:38 +02:00
Miroslav Lichvar
d322c8e6e5 doc: improve description of chronyc -n option 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
3dec266dd5 client: indicate truncated addresses/hostnames
Add symbol > to the end of the resolved hostname or address when it's
truncated.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
862938cc79 client: truncate long hostnames in clients output 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
ee396702f2 client: print intervals in seconds up to 1200 seconds
This covers random variations in the default maximum polling interval.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
316d50d6f1 sources: optimize SRC_ReportSource() a bit
Remove unnecessary memset() call and use the default case of the switch
to report the unreachable state.
2016-07-20 09:34:11 +02:00
Miroslav Lichvar
5e92aaf8a5 addressing: pad IPAddr struct explicitly 2016-07-20 09:34:11 +02:00
Miroslav Lichvar
7ffe59a734 util: round up when converting to 32-bit NTP values
NTP clients shouldn't get root delay and dispersion smaller than the
server's values.
2016-07-20 09:17:24 +02:00
Miroslav Lichvar
6cd558398a ntp: add offset option
Add offset option to the server/pool/peer directive. It specifies a
correction which will be applied to offsets measured with the NTP
source. It's particularly useful to compensate for a known asymmetry in
network delay or timestamping errors.
2016-06-28 15:40:58 +02:00
Miroslav Lichvar
632cd1a177 client: rework error printing for unsupported source options 2016-06-28 13:26:09 +02:00
Miroslav Lichvar
223ad0e8aa conf: fix parsing of refclock directive
Don't accept refclock directive which has as the last argument an option
that requires a value.
2016-06-28 13:23:27 +02:00
Miroslav Lichvar
f8bd9ab378 cmdparse: remove CPS_Status
Remove command and option specific error codes. Return zero on any
failure.
2016-06-27 14:52:55 +02:00
Miroslav Lichvar
d78e8f096c cmdparse: refactor CPS_ParseNTPSourceAdd() 2016-06-27 14:52:49 +02:00
Miroslav Lichvar
57fc2ff1be sched: add support for output file event
This allows waiting for non-blocking write operations.
2016-06-23 11:45:49 +02:00
Miroslav Lichvar
d8d096aa54 sched: don't keep prepared fd_set
Instead of copying a prepared fd_set to the fd_set used by select(),
fill it from scratch according to the array of file handlers before each
select() call. This should make the code simpler and save some memory
when other events are supported.
2016-06-23 11:34:00 +02:00
Miroslav Lichvar
0a10545314 sched: rework file handling API
Replace SCH_*InputFileHandler() functions with more general
SCH_*FileHandler(), where events are specified as a new parameter and
which will later support other file events, e.g. file ready for ouput
and exception.

The file handlers have two new parameters: file descriptor and event.
2016-06-23 11:33:54 +02:00
Miroslav Lichvar
aeb57a36b2 logging: fix LOG_MESSAGE macro to not use semicolon 2016-06-16 17:15:36 +02:00
117 changed files with 8386 additions and 3254 deletions

View File

@@ -37,7 +37,7 @@ HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
sys.o smooth.o tempcomp.o util.o $(HASH_OBJ)
smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@
@@ -63,17 +63,11 @@ chronyd : $(OBJS) $(EXTRA_OBJS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
client.o : client.c
$(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
$(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
$(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $<
distclean : clean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
$(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
clean :
-rm -f *.o *.s chronyc chronyd core *~
@@ -115,10 +109,14 @@ install-docs :
%.s : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -S $<
check : chronyd chronyc
quickcheck : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run
check : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run -i 20 -m 2
Makefile : Makefile.in configure
@echo
@echo Makefile needs to be regenerated, run ./configure

78
NEWS
View File

@@ -1,3 +1,81 @@
New in version 3.2
==================
Enhancements
------------
* Improve stability with NTP sources and reference clocks
* Improve support for NTP interleaved modes
* Control frequency of system clock on macOS 10.13 and later
* Set TAI-UTC offset of system clock with leapsectz directive
* Add support for new HW timestamping options added in Linux 4.13
* Add rxfilter option to hwtimestamp directive
* Add extpps option to PHC refclock to timestamp external PPS signal
* Add pps option to refclock directive to treat any refclock as PPS
* Add width option to refclock directive to filter wrong pulse edges
* Add -x option to disable control of system clock
* Add -l option to log to specified file instead of syslog
* Allow multiple command-line options to be specified together
* Allow starting without root privileges with -Q option
* Update seccomp filter for new glibc versions
* Dump history on exit by default with dumpdir directive
* Use hardening compiler options by default
Bug fixes
---------
* Don't drop PHC samples with low-resolution system clock
* Ignore outliers in PHC tracking, RTC tracking, manual input
* Increase polling interval when peer is not responding
* Exit with error message when include directive fails
* Don't allow slash after hostname in allow/deny directive/command
* Try to connect to all addresses in chronyc before giving up
New in version 3.1
==================
Enhancements
------------
* Add support for precise cross timestamping of PHC on Linux
* Add minpoll, precision, nocrossts options to hwtimestamp directive
* Add rawmeasurements option to log directive and modify measurements
option to log only valid measurements from synchronised sources
* Allow sub-second polling interval with NTP sources
Bug fixes
---------
* Fix time smoothing in interleaved mode
New in version 3.0
==================
Enhancements
------------
* Add support for software and hardware timestamping on Linux
* Add support for client/server and symmetric interleaved modes
* Add support for MS-SNTP authentication in Samba
* Add support for truncated MACs in NTPv4 packets
* Estimate and correct for asymmetric network jitter
* Increase default minsamples and polltarget to improve stability
with very low jitter
* Add maxjitter directive to limit source selection by jitter
* Add offset option to server/pool/peer directive
* Add maxlockage option to refclock directive
* Add -t option to chronyd to exit after specified time
* Add partial protection against replay attacks on symmetric mode
* Don't reset polling interval when switching sources to online state
* Allow rate limiting with very short intervals
* Improve maximum server throughput on Linux and NetBSD
* Remove dump files after start
* Add tab-completion to chronyc with libedit/readline
* Add ntpdata command to print details about NTP measurements
* Allow all source options to be set in add server/peer command
* Indicate truncated addresses/hostnames in chronyc output
* Print reference IDs as hexadecimal numbers to avoid confusion with
IPv4 addresses
Bug fixes
---------
* Fix crash with disabled asynchronous name resolving
New in version 2.4.1
====================

48
README
View File

@@ -14,10 +14,10 @@ intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
Typical accuracy between two machines on a LAN is in tens, or a few
hundreds, of microseconds; over the Internet, accuracy is typically
within a few milliseconds. With a good hardware reference clock
sub-microsecond accuracy is possible.
Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of
microseconds. With hardware timestamping, or a hardware reference clock,
sub-microsecond accuracy may be possible.
Two programs are included in chrony, chronyd is a daemon that can be
started at boot time and chronyc is a command-line interface program
@@ -27,7 +27,7 @@ operating parameters whilst it is running.
What will chrony run on?
========================
The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one
of the existing system-specific drivers and look into the quirks of
@@ -95,19 +95,12 @@ License
chrony is distributed under the GNU General Public License version 2.
Author
======
Authors
=======
Richard P. Curnow <rc@rc0.org.uk>
Maintainers
===========
Miroslav Lichvar <mlichvar@redhat.com>
Acknowledgements
================
@@ -118,6 +111,9 @@ implementation has been used to check the details of the protocol.
The following people have provided patches and other major contributions
to the program :
Lonnie Abelbeck <lonnie@abelbeck.com>
Patch to add tab-completion to chronyc
Benny Lyne Amorsen <benny@amorsen.dk>
Patch to add minstratum option
@@ -139,7 +135,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Entries in contrib directory
Bryan Christianson <bryan@whatroute.net>
Support for Mac OS X
Support for macOS
Support for privilege separation
Entries in contrib directory
@@ -198,18 +194,6 @@ Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi>
Advice on configuring for BSD/386
Miroslav Lichvar <mlichvar@redhat.com>
Reference clock support
IPv6 support
Linux capabilities support
Leap second support
Improved source selection
Improved sample history trimming
Improved polling interval adjustment
Improved stability with temporary asymmetric delays
Temperature compensation
Many other bug fixes and improvements
Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100
@@ -219,6 +203,9 @@ Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc
@@ -228,6 +215,9 @@ Andreas Piesk <apiesk@virbus.de>
Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts
Bill Unruh <unruh@physics.ubc.ca>
Advice on statistics
Stephen Wadeley <swadeley@redhat.com>
Improvements to man pages
@@ -244,5 +234,5 @@ Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86
Many other people have contributed bug reports and suggestions. I'm
sorry I can't identify all of you individually.
Many other people have contributed bug reports and suggestions. We are sorry
we cannot identify all of you individually.

View File

@@ -42,6 +42,7 @@ typedef struct {
uint8_t in6[16];
} addr;
uint16_t family;
uint16_t _pad;
} IPAddr;
typedef struct {
@@ -49,8 +50,11 @@ typedef struct {
unsigned short port;
} NTP_Remote_Address;
#define INVALID_IF_INDEX -1
typedef struct {
IPAddr ip_addr;
int if_index;
int sock_fd;
} NTP_Local_Address;

79
candm.h
View File

@@ -94,14 +94,17 @@
#define REQ_SERVER_STATS 54
#define REQ_CLIENT_ACCESSES_BY_INDEX2 55
#define REQ_LOCAL2 56
#define N_REQUEST_TYPES 57
#define REQ_NTP_DATA 57
#define REQ_ADD_SERVER2 58
#define REQ_ADD_PEER2 59
#define N_REQUEST_TYPES 60
/* Structure used to exchange timevals independent on size of time_t */
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
uint32_t tv_sec_high;
uint32_t tv_sec_low;
uint32_t tv_nsec;
} Timeval;
} Timespec;
/* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff
@@ -200,12 +203,12 @@ typedef struct {
} REQ_Modify_Makestep;
typedef struct {
Timeval ts;
Timespec ts;
int32_t EOR;
} REQ_Logon;
typedef struct {
Timeval ts;
Timespec ts;
int32_t EOR;
} REQ_Settime;
@@ -246,6 +249,7 @@ typedef struct {
#define REQ_ADDSRC_NOSELECT 0x10
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
typedef struct {
IPAddr ip_addr;
@@ -253,9 +257,17 @@ typedef struct {
int32_t minpoll;
int32_t maxpoll;
int32_t presend_minpoll;
uint32_t min_stratum;
uint32_t poll_target;
uint32_t version;
uint32_t max_sources;
int32_t min_samples;
int32_t max_samples;
uint32_t authkey;
Float max_delay;
Float max_delay_ratio;
Float max_delay_dev_ratio;
Float offset;
uint32_t flags;
int32_t EOR;
} REQ_NTP_Source;
@@ -309,6 +321,11 @@ typedef struct {
int32_t EOR;
} REQ_SmoothTime;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_NTPData;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -345,8 +362,9 @@ typedef struct {
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types), new flags in NTP source request and report,
new commands: refresh, serverstats
(using new request/reply types) and manual timestamp, new fields and flags
in NTP source request and report, new commands: ntpdata, refresh,
serverstats
*/
#define PROTO_VERSION_NUMBER 6
@@ -409,6 +427,7 @@ typedef struct {
REQ_ManualDelete manual_delete;
REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -442,7 +461,9 @@ typedef struct {
#define RPY_SMOOTHING 13
#define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define N_REPLY_TYPES 16
#define RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17
#define N_REPLY_TYPES 18
/* Status codes */
#define STT_SUCCESS 0
@@ -512,7 +533,7 @@ typedef struct {
IPAddr ip_addr;
uint16_t stratum;
uint16_t leap_status;
Timeval ref_time;
Timespec ref_time;
Float current_correction;
Float last_offset;
Float rms_offset;
@@ -540,7 +561,7 @@ typedef struct {
} RPY_Sourcestats;
typedef struct {
Timeval ref_time;
Timespec ref_time;
uint16_t n_samples;
uint16_t n_runs;
uint32_t span_seconds;
@@ -550,7 +571,7 @@ typedef struct {
} RPY_Rtc;
typedef struct {
uint32_t centiseconds;
Float offset;
Float dfreq_ppm;
Float new_afreq_ppm;
int32_t EOR;
@@ -590,7 +611,7 @@ typedef struct {
#define MAX_MANUAL_LIST_SAMPLES 16
typedef struct {
Timeval when;
Timespec when;
Float slewed_offset;
Float orig_offset;
Float residual;
@@ -624,6 +645,39 @@ typedef struct {
int32_t EOR;
} RPY_Smoothing;
#define RPY_NTP_FLAGS_TESTS 0x3ff
#define RPY_NTP_FLAG_INTERLEAVED 0x4000
#define RPY_NTP_FLAG_AUTHENTICATED 0x8000
typedef struct {
IPAddr remote_addr;
IPAddr local_addr;
uint16_t remote_port;
uint8_t leap;
uint8_t version;
uint8_t mode;
uint8_t stratum;
int8_t poll;
int8_t precision;
Float root_delay;
Float root_dispersion;
uint32_t ref_id;
Timespec ref_time;
Float offset;
Float peer_delay;
Float peer_dispersion;
Float response_time;
Float jitter_asymmetry;
uint16_t flags;
uint8_t tx_tss_char;
uint8_t rx_tss_char;
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;
typedef struct {
uint8_t version;
uint8_t pkt_type;
@@ -652,6 +706,7 @@ typedef struct {
RPY_ManualList manual_list;
RPY_Activity activity;
RPY_Smoothing smoothing;
RPY_NTPData ntp_data;
} data; /* Reply specific parameters */
} CMD_Reply;

714
client.c

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,7 @@
#include "clientlog.h"
#include "conf.h"
#include "memory.h"
#include "ntp.h"
#include "reports.h"
#include "util.h"
#include "logging.h"
@@ -57,6 +58,8 @@ typedef struct {
int8_t cmd_rate;
int8_t ntp_timeout_rate;
uint8_t flags;
NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts;
} Record;
/* Hash table of records, there is a fixed number of records per slot */
@@ -83,6 +86,10 @@ static unsigned int max_slots;
#define TS_FRAC 4
#define INVALID_TS 0
/* Static offset included in conversion to the fixed-point timestamps to
randomise their alignment */
static uint32_t ts_offset;
/* Request rates are saved in the record as 8-bit scaled log2 values */
#define RATE_SCALE 4
#define MIN_RATE (-14 * RATE_SCALE)
@@ -92,7 +99,7 @@ static unsigned int max_slots;
number of tokens spent on response are determined from configured
minimum inverval between responses (in log2) and burst length. */
#define MIN_LIMIT_INTERVAL (-TS_FRAC)
#define MIN_LIMIT_INTERVAL (-15 - TS_FRAC)
#define MAX_LIMIT_INTERVAL 12
#define MIN_LIMIT_BURST 1
#define MAX_LIMIT_BURST 255
@@ -102,7 +109,8 @@ static uint16_t max_cmd_tokens;
static uint16_t ntp_tokens_per_packet;
static uint16_t cmd_tokens_per_packet;
/* Reduction of token rates to avoid overflow of 16-bit counters */
/* Reduction of token rates to avoid overflow of 16-bit counters. Negative
shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
static int ntp_token_shift;
static int cmd_token_shift;
@@ -120,6 +128,9 @@ static int cmd_leak_rate;
/* Flag indicating whether the last response was dropped */
#define FLAG_NTP_DROPPED 0x1
/* NTP limit interval in log2 */
static int ntp_limit_interval;
/* Flag indicating whether facility is turned on or not */
static int active;
@@ -130,6 +141,8 @@ static uint32_t total_ntp_drops;
static uint32_t total_cmd_drops;
static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U
/* ================================================== */
static int expand_hashtable(void);
@@ -155,7 +168,7 @@ get_record(IPAddr *ip)
time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record;
if (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6)
if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
return NULL;
while (1) {
@@ -206,6 +219,8 @@ get_record(IPAddr *ip)
record->ntp_rate = record->cmd_rate = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE;
record->flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts);
return record;
}
@@ -266,16 +281,23 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST);
/* Find smallest shift with which the maximum number fits in 16 bits */
for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
break;
if (interval >= -TS_FRAC) {
/* Find the smallest shift with which the maximum number fits in 16 bits */
for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) {
if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16)
break;
}
} else {
/* Coarse rate limiting */
*token_shift = interval + TS_FRAC;
*tokens_per_packet = 1;
burst = MAX(1U << -*token_shift, burst);
}
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
*max_tokens = *tokens_per_packet * burst;
DEBUG_LOG(LOGF_ClientLog, "Tokens max %d packet %d shift %d",
DEBUG_LOG("Tokens max %d packet %d shift %d",
*max_tokens, *tokens_per_packet, *token_shift);
}
@@ -290,11 +312,13 @@ CLG_Initialise(void)
ntp_tokens_per_packet = cmd_tokens_per_packet = 0;
ntp_token_shift = cmd_token_shift = 0;
ntp_leak_rate = cmd_leak_rate = 0;
ntp_limit_interval = MIN_LIMIT_INTERVAL;
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) {
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
&ntp_token_shift);
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
}
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) {
@@ -306,7 +330,7 @@ CLG_Initialise(void)
active = !CNF_GetNoClientLog();
if (!active) {
if (ntp_leak_rate || cmd_leak_rate)
LOG_FATAL(LOGF_ClientLog, "ratelimit cannot be used with noclientlog");
LOG_FATAL("ratelimit cannot be used with noclientlog");
return;
}
@@ -320,6 +344,9 @@ CLG_Initialise(void)
records = NULL;
expand_hashtable();
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
}
/* ================================================== */
@@ -336,23 +363,30 @@ CLG_Finalise(void)
/* ================================================== */
static uint32_t
get_ts_from_timeval(struct timeval *tv)
get_ts_from_timespec(struct timespec *ts)
{
uint32_t sec = tv->tv_sec, usec = tv->tv_usec;
uint32_t sec = ts->tv_sec, nsec = ts->tv_nsec;
return sec << TS_FRAC | (4295U * usec - (usec >> 5)) >> (32 - TS_FRAC);
nsec += ts_offset;
if (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
sec++;
}
/* This is fast and accurate enough */
return sec << TS_FRAC | (140740U * (nsec >> 15)) >> (32 - TS_FRAC);
}
/* ================================================== */
static void
update_record(struct timeval *now, uint32_t *last_hit, uint32_t *hits,
update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
{
uint32_t interval, now_ts, prev_hit, new_tokens;
int interval2;
now_ts = get_ts_from_timeval(now);
now_ts = get_ts_from_timespec(now);
prev_hit = *last_hit;
*last_hit = now_ts;
@@ -363,7 +397,12 @@ update_record(struct timeval *now, uint32_t *last_hit, uint32_t *hits,
if (prev_hit == INVALID_TS || (int32_t)interval < 0)
return;
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift);
if (token_shift >= 0)
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift);
else if (now_ts - prev_hit > max_tokens)
new_tokens = max_tokens;
else
new_tokens = (now_ts - prev_hit) << -token_shift;
*tokens = MIN(*tokens + new_tokens, max_tokens);
/* Convert the interval to scaled and rounded log2 */
@@ -405,15 +444,26 @@ get_index(Record *record)
/* ================================================== */
int
CLG_LogNTPAccess(IPAddr *client, struct timeval *now)
CLG_GetClientIndex(IPAddr *client)
{
Record *record;
record = get_record(client);
if (record == NULL)
return -1;
return get_index(record);
}
/* ================================================== */
int
CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
{
Record *record;
total_ntp_hits++;
if (!active)
return -1;
record = get_record(client);
if (record == NULL)
return -1;
@@ -425,7 +475,7 @@ CLG_LogNTPAccess(IPAddr *client, struct timeval *now)
record->flags & FLAG_NTP_DROPPED ?
&record->ntp_timeout_rate : &record->ntp_rate);
DEBUG_LOG(LOGF_ClientLog, "NTP hits %"PRIu32" rate %d trate %d tokens %d",
DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d",
record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
record->ntp_tokens);
@@ -435,15 +485,12 @@ CLG_LogNTPAccess(IPAddr *client, struct timeval *now)
/* ================================================== */
int
CLG_LogCommandAccess(IPAddr *client, struct timeval *now)
CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
{
Record *record;
total_cmd_hits++;
if (!active)
return -1;
record = get_record(client);
if (record == NULL)
return -1;
@@ -452,7 +499,7 @@ CLG_LogCommandAccess(IPAddr *client, struct timeval *now)
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
&record->cmd_rate);
DEBUG_LOG(LOGF_ClientLog, "Cmd hits %"PRIu32" rate %d tokens %d",
DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d",
record->cmd_hits, record->cmd_rate, record->cmd_tokens);
return get_index(record);
@@ -552,7 +599,27 @@ CLG_LimitCommandResponseRate(int index)
/* ================================================== */
extern int
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
{
Record *record;
record = ARR_GetElement(records, index);
*rx_ts = &record->ntp_rx_ts;
*tx_ts = &record->ntp_tx_ts;
}
/* ================================================== */
int
CLG_GetNtpMinPoll(void)
{
return ntp_limit_interval;
}
/* ================================================== */
int
CLG_GetNumberOfIndices(void)
{
if (!active)
@@ -586,7 +653,7 @@ static uint32_t get_last_ago(uint32_t x, uint32_t y)
/* ================================================== */
int
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now)
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now)
{
Record *record;
uint32_t now_ts;
@@ -599,7 +666,7 @@ CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *repo
if (record->ip_addr.family == IPADDR_UNSPEC)
return 0;
now_ts = get_ts_from_timeval(now);
now_ts = get_ts_from_timespec(now);
report->ip_addr = record->ip_addr;
report->ntp_hits = record->ntp_hits;

View File

@@ -33,15 +33,18 @@
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern int CLG_LogNTPAccess(IPAddr *client, struct timeval *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timeval *now);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
extern int CLG_GetNtpMinPoll(void);
/* And some reporting functions, for use by chronyc. */
extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */

166
cmdmon.c
View File

@@ -133,6 +133,9 @@ static const char permissions[] = {
PERMIT_AUTH, /* SERVER_STATS */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
PERMIT_AUTH, /* LOCAL2 */
PERMIT_AUTH, /* NTP_DATA */
PERMIT_AUTH, /* ADD_SERVER2 */
PERMIT_AUTH, /* ADD_PEER2 */
};
/* ================================================== */
@@ -143,7 +146,7 @@ static ADF_AuthTable access_auth_table;
/* ================================================== */
/* Forward prototypes */
static void read_from_cmd_socket(void *anything);
static void read_from_cmd_socket(int sock_fd, int event, void *anything);
/* ================================================== */
@@ -158,7 +161,7 @@ prepare_socket(int family, int port_number)
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not open %s command socket : %s",
LOG(LOGS_ERR, "Could not open %s command socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
return -1;
}
@@ -169,14 +172,14 @@ prepare_socket(int family, int port_number)
if (family != AF_UNIX) {
/* Allow reuse of port number */
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
LOG(LOGS_ERR, "Could not set reuseaddr socket options");
/* Don't quit - we might survive anyway */
}
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option");
LOG(LOGS_ERR, "Could not set free bind socket option");
}
#endif
@@ -185,7 +188,7 @@ prepare_socket(int family, int port_number)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not request IPV6_V6ONLY socket option");
LOG(LOGS_ERR, "Could not request IPV6_V6ONLY socket option");
}
#endif
}
@@ -227,7 +230,7 @@ prepare_socket(int family, int port_number)
my_addr.un.sun_family = family;
if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s",
CNF_GetBindCommandPath()) >= sizeof (my_addr.un.sun_path))
LOG_FATAL(LOGF_CmdMon, "Unix socket path too long");
LOG_FATAL("Unix socket path too long");
unlink(my_addr.un.sun_path);
break;
default:
@@ -235,14 +238,14 @@ prepare_socket(int family, int port_number)
}
if (bind(sock_fd, &my_addr.sa, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not bind %s command socket : %s",
LOG(LOGS_ERR, "Could not bind %s command socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd);
return -1;
}
/* Register handler for read events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_cmd_socket, (void *)(long)sock_fd);
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_from_cmd_socket, NULL);
return sock_fd;
}
@@ -312,7 +315,7 @@ CAM_Initialise(int family)
&& sock_fd6 < 0
#endif
) {
LOG_FATAL(LOGF_CmdMon, "Could not open any command socket");
LOG_FATAL("Could not open any command socket");
}
access_auth_table = ADF_CreateTable();
@@ -325,19 +328,19 @@ void
CAM_Finalise(void)
{
if (sock_fdu >= 0) {
SCH_RemoveInputFileHandler(sock_fdu);
SCH_RemoveFileHandler(sock_fdu);
close(sock_fdu);
unlink(CNF_GetBindCommandPath());
}
sock_fdu = -1;
if (sock_fd4 >= 0) {
SCH_RemoveInputFileHandler(sock_fd4);
SCH_RemoveFileHandler(sock_fd4);
close(sock_fd4);
}
sock_fd4 = -1;
#ifdef FEAT_IPV6
if (sock_fd6 >= 0) {
SCH_RemoveInputFileHandler(sock_fd6);
SCH_RemoveFileHandler(sock_fd6);
close(sock_fd6);
}
sock_fd6 = -1;
@@ -393,12 +396,12 @@ transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to)
&where_to->sa, addrlen);
if (status < 0) {
DEBUG_LOG(LOGF_CmdMon, "Could not send to %s fd %d : %s",
DEBUG_LOG("Could not send to %s fd %d : %s",
UTI_SockaddrToString(&where_to->sa), sock_fd, strerror(errno));
return;
}
DEBUG_LOG(LOGF_CmdMon, "Sent %d bytes to %s fd %d", status,
DEBUG_LOG("Sent %d bytes to %s fd %d", status,
UTI_SockaddrToString(&where_to->sa), sock_fd);
}
@@ -564,15 +567,14 @@ handle_modify_makestep(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
{
struct timeval ts;
long offset_cs;
double dfreq_ppm, new_afreq_ppm;
UTI_TimevalNetworkToHost(&rx_message->data.settime.ts, &ts);
struct timespec ts;
double offset, dfreq_ppm, new_afreq_ppm;
UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts);
if (!MNL_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED);
} else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP);
tx_message->data.manual_timestamp.centiseconds = htonl((int32_t)offset_cs);
} else if (MNL_AcceptTimestamp(&ts, &offset, &dfreq_ppm, &new_afreq_ppm)) {
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP2);
tx_message->data.manual_timestamp.offset = UTI_FloatHostToNetwork(offset);
tx_message->data.manual_timestamp.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm);
tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm);
} else {
@@ -634,7 +636,7 @@ static void
handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SourceReport report;
struct timeval now_corr;
struct timespec now_corr;
/* Get data */
SCH_GetLastEventTime(&now_corr, NULL, NULL);
@@ -777,26 +779,29 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
params.min_stratum = ntohl(rx_message->data.ntp_source.min_stratum);
params.poll_target = ntohl(rx_message->data.ntp_source.poll_target);
params.version = ntohl(rx_message->data.ntp_source.version);
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
params.max_delay_dev_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
/* not transmitted in cmdmon protocol yet */
params.min_stratum = SRC_DEFAULT_MINSTRATUM;
params.poll_target = SRC_DEFAULT_POLLTARGET;
params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
params.version = NTP_VERSION;
params.max_sources = SRC_DEFAULT_MAXSOURCES;
params.min_samples = SRC_DEFAULT_MINSAMPLES;
params.max_samples = SRC_DEFAULT_MAXSAMPLES;
status = NSR_AddSource(&rem_addr, type, &params);
switch (status) {
@@ -868,7 +873,7 @@ handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
double dfreq;
dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq);
LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6);
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta freq of %.3fppm", dfreq);
LOG(LOGS_INFO, "Accumulated delta freq of %.3fppm", dfreq);
}
/* ================================================== */
@@ -881,7 +886,7 @@ handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
sec = (int32_t)ntohl(rx_message->data.doffset.sec);
usec = (int32_t)ntohl(rx_message->data.doffset.usec);
doffset = (double) sec + 1.0e-6 * (double) usec;
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset);
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
LCL_AccumulateOffset(doffset, 0.0);
}
@@ -898,7 +903,7 @@ handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPHostToNetwork(&rpt.ip_addr, &tx_message->data.tracking.ip_addr);
tx_message->data.tracking.stratum = htons(rpt.stratum);
tx_message->data.tracking.leap_status = htons(rpt.leap_status);
UTI_TimevalHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time);
UTI_TimespecHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time);
tx_message->data.tracking.current_correction = UTI_FloatHostToNetwork(rpt.current_correction);
tx_message->data.tracking.last_offset = UTI_FloatHostToNetwork(rpt.last_offset);
tx_message->data.tracking.rms_offset = UTI_FloatHostToNetwork(rpt.rms_offset);
@@ -916,7 +921,7 @@ static void
handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SmoothingReport report;
struct timeval now;
struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL);
@@ -940,7 +945,7 @@ handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_smoothtime(CMD_Request *rx_message, CMD_Reply *tx_message)
{
struct timeval now;
struct timespec now;
int option;
if (!SMT_IsEnabled()) {
@@ -971,7 +976,7 @@ handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int status;
RPT_SourcestatsReport report;
struct timeval now_corr;
struct timespec now_corr;
SCH_GetLastEventTime(&now_corr, NULL, NULL);
status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index),
@@ -1004,7 +1009,7 @@ handle_rtcreport(CMD_Request *rx_message, CMD_Reply *tx_message)
status = RTC_GetReport(&report);
if (status) {
tx_message->reply = htons(RPY_RTC);
UTI_TimevalHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time);
UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time);
tx_message->data.rtc.n_samples = htons(report.n_samples);
tx_message->data.rtc.n_runs = htons(report.n_runs);
tx_message->data.rtc.span_seconds = htonl(report.span_seconds);
@@ -1041,7 +1046,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
RPY_ClientAccesses_Client *client;
int n_indices;
uint32_t i, j, req_first_index, req_n_clients;
struct timeval now;
struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL);
@@ -1100,7 +1105,7 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.manual_list.n_samples = htonl(n_samples);
for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i];
UTI_TimevalHostToNetwork(&report[i].when, &sample->when);
UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
sample->slewed_offset = UTI_FloatHostToNetwork(report[i].slewed_offset);
sample->orig_offset = UTI_FloatHostToNetwork(report[i].orig_offset);
sample->residual = UTI_FloatHostToNetwork(report[i].residual);
@@ -1185,38 +1190,81 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
}
/* ================================================== */
static void
handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_NTPReport report;
UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &report.remote_addr);
if (!NSR_GetNTPReport(&report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_NTP_DATA);
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
tx_message->data.ntp_data.leap = report.leap;
tx_message->data.ntp_data.version = report.version;
tx_message->data.ntp_data.mode = report.mode;
tx_message->data.ntp_data.stratum = report.stratum;
tx_message->data.ntp_data.poll = report.poll;
tx_message->data.ntp_data.precision = report.precision;
tx_message->data.ntp_data.root_delay = UTI_FloatHostToNetwork(report.root_delay);
tx_message->data.ntp_data.root_dispersion = UTI_FloatHostToNetwork(report.root_dispersion);
tx_message->data.ntp_data.ref_id = htonl(report.ref_id);
UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.ntp_data.ref_time);
tx_message->data.ntp_data.offset = UTI_FloatHostToNetwork(report.offset);
tx_message->data.ntp_data.peer_delay = UTI_FloatHostToNetwork(report.peer_delay);
tx_message->data.ntp_data.peer_dispersion = UTI_FloatHostToNetwork(report.peer_dispersion);
tx_message->data.ntp_data.response_time = UTI_FloatHostToNetwork(report.response_time);
tx_message->data.ntp_data.jitter_asymmetry = UTI_FloatHostToNetwork(report.jitter_asymmetry);
tx_message->data.ntp_data.flags = htons((report.tests & RPY_NTP_FLAGS_TESTS) |
(report.interleaved ? RPY_NTP_FLAG_INTERLEAVED : 0) |
(report.authenticated ? RPY_NTP_FLAG_AUTHENTICATED : 0));
tx_message->data.ntp_data.tx_tss_char = report.tx_tss_char;
tx_message->data.ntp_data.rx_tss_char = report.rx_tss_char;
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
/* ================================================== */
/* Read a packet and process it */
static void
read_from_cmd_socket(void *anything)
read_from_cmd_socket(int sock_fd, int event, void *anything)
{
CMD_Request rx_message;
CMD_Reply tx_message;
int status, read_length, expected_length, rx_message_length;
int localhost, allowed, sock_fd, log_index;
int localhost, allowed, log_index;
union sockaddr_all where_from;
socklen_t from_length;
IPAddr remote_ip;
unsigned short remote_port, rx_command;
struct timeval now, cooked_now;
struct timespec now, cooked_now;
rx_message_length = sizeof(rx_message);
from_length = sizeof(where_from);
sock_fd = (long)anything;
status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0,
&where_from.sa, &from_length);
if (status < 0) {
LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket %d",
LOG(LOGS_WARN, "Error [%s] reading from control socket %d",
strerror(errno), sock_fd);
return;
}
if (from_length > sizeof (where_from) ||
from_length <= sizeof (where_from.sa.sa_family)) {
DEBUG_LOG(LOGF_CmdMon, "Read command packet without source address");
DEBUG_LOG("Read command packet without source address");
return;
}
@@ -1251,7 +1299,7 @@ read_from_cmd_socket(void *anything)
assert(0);
}
DEBUG_LOG(LOGF_CmdMon, "Received %d bytes from %s fd %d",
DEBUG_LOG("Received %d bytes from %s fd %d",
status, UTI_SockaddrToString(&where_from.sa), sock_fd);
if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) {
@@ -1270,7 +1318,7 @@ read_from_cmd_socket(void *anything)
/* We don't know how to process anything like this or an error reply
would be larger than the request */
DEBUG_LOG(LOGF_CmdMon, "Command packet dropped");
DEBUG_LOG("Command packet dropped");
return;
}
@@ -1292,7 +1340,7 @@ read_from_cmd_socket(void *anything)
tx_message.pad5 = 0;
if (rx_message.version != PROTO_VERSION_NUMBER) {
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid version (%d != %d)",
DEBUG_LOG("Command packet has invalid version (%d != %d)",
rx_message.version, PROTO_VERSION_NUMBER);
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
@@ -1304,7 +1352,7 @@ read_from_cmd_socket(void *anything)
if (rx_command >= N_REQUEST_TYPES ||
expected_length < (int)offsetof(CMD_Request, data)) {
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid command %d", rx_command);
DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID);
transmit_reply(&tx_message, &where_from);
@@ -1312,7 +1360,7 @@ read_from_cmd_socket(void *anything)
}
if (read_length < expected_length) {
DEBUG_LOG(LOGF_CmdMon, "Command packet is too short (%d < %d)", read_length,
DEBUG_LOG("Command packet is too short (%d < %d)", read_length,
expected_length);
tx_message.status = htons(STT_BADPKTLENGTH);
@@ -1327,7 +1375,7 @@ read_from_cmd_socket(void *anything)
/* Don't reply to all requests from hosts other than localhost if the rate
is excessive */
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) {
DEBUG_LOG(LOGF_CmdMon, "Command packet discarded to limit response rate");
DEBUG_LOG("Command packet discarded to limit response rate");
return;
}
@@ -1477,11 +1525,11 @@ read_from_cmd_socket(void *anything)
handle_cmdaccheck(&rx_message, &tx_message);
break;
case REQ_ADD_SERVER:
case REQ_ADD_SERVER2:
handle_add_source(NTP_SERVER, &rx_message, &tx_message);
break;
case REQ_ADD_PEER:
case REQ_ADD_PEER2:
handle_add_source(NTP_PEER, &rx_message, &tx_message);
break;
@@ -1573,8 +1621,12 @@ read_from_cmd_socket(void *anything)
handle_server_stats(&rx_message, &tx_message);
break;
case REQ_NTP_DATA:
handle_ntp_data(&rx_message, &tx_message);
break;
default:
DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command);
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}

View File

@@ -39,186 +39,115 @@
/* ================================================== */
CPS_Status
int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
int n, done;
CPS_Status result;
int n;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
src->params.online = 1;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = 0;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.authkey = INACTIVE_AUTHKEY;
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.online = 1;
src->params.auto_offline = 0;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = NTP_VERSION;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.sel_options = 0;
src->params.offset = 0.0;
result = CPS_Success;
hostname = line;
line = CPS_SplitWord(line);
if (!*hostname) {
result = CPS_BadHost;
} else {
src->name = hostname;
if (!*hostname)
return 0;
/* Parse subfields */
done = 0;
do {
cmd = line;
line = CPS_SplitWord(line);
src->name = hostname;
if (*cmd) {
if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1) {
result = CPS_BadPort;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) {
result = CPS_BadMinpoll;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) {
result = CPS_BadMaxpoll;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) {
result = CPS_BadPresend;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) {
result = CPS_BadMaxdelaydevratio;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) {
result = CPS_BadMaxdelayratio;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) {
result = CPS_BadMaxdelay;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) {
result = CPS_BadKey;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0;
/* Parse options */
for (; *line; line += n) {
cmd = line;
line = CPS_SplitWord(line);
n = 0;
} else if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) {
result = CPS_BadMinstratum;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) {
result = CPS_BadPolltarget;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1) {
result = CPS_BadVersion;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) {
result = CPS_BadMaxsources;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1) {
result = CPS_BadMinsamples;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1) {
result = CPS_BadMaxsamples;
done = 1;
} else {
line += n;
}
} else {
result = CPS_BadOption;
done = 1;
}
} else {
done = 1;
}
} while (!done);
if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY)
return 0;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "offset")) {
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
} else {
return 0;
}
}
return result;
return 1;
}
/* ================================================== */
@@ -256,71 +185,6 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
return 1;
}
/* ================================================== */
void
CPS_StatusToString(CPS_Status status, char *dest, int len)
{
const char *s = NULL;
if (len > 0)
dest[0] = '\0';
switch (status) {
case CPS_Success:
return;
case CPS_BadOption:
s = "server/peer/pool option";
break;
case CPS_BadHost:
s = "address";
break;
case CPS_BadPort:
s = "port";
break;
case CPS_BadMinpoll:
s = "minpoll";
break;
case CPS_BadMaxpoll:
s = "maxpoll";
break;
case CPS_BadPresend:
s = "presend";
break;
case CPS_BadMaxdelaydevratio:
s = "maxdelaydevratio";
break;
case CPS_BadMaxdelayratio:
s = "maxdelayratio";
break;
case CPS_BadMaxdelay:
s = "maxdelay";
break;
case CPS_BadKey:
s = "key";
break;
case CPS_BadMinstratum:
s = "minstratum";
break;
case CPS_BadPolltarget:
s = "polltarget";
break;
case CPS_BadVersion:
s = "version";
break;
case CPS_BadMaxsources:
s = "maxsources";
break;
case CPS_BadMinsamples:
s = "minsamples";
break;
case CPS_BadMaxsamples:
s = "maxsamples";
break;
}
snprintf(dest, len, "Invalid %s", s);
}
/* ================================================== */

View File

@@ -30,26 +30,6 @@
#include "srcparams.h"
#include "addressing.h"
typedef enum {
CPS_Success,
CPS_BadOption,
CPS_BadHost,
CPS_BadPort,
CPS_BadMinpoll,
CPS_BadMaxpoll,
CPS_BadPresend,
CPS_BadMaxdelaydevratio,
CPS_BadMaxdelayratio,
CPS_BadMaxdelay,
CPS_BadKey,
CPS_BadMinstratum,
CPS_BadPolltarget,
CPS_BadVersion,
CPS_BadMaxsources,
CPS_BadMinsamples,
CPS_BadMaxsamples,
} CPS_Status;
typedef struct {
char *name;
unsigned short port;
@@ -57,14 +37,11 @@ typedef struct {
} CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
/* Get a string describing error status */
extern void CPS_StatusToString(CPS_Status status, char *dest, int len);
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);

230
conf.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016
* Copyright (C) Miroslav Lichvar 2009-2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -57,6 +57,7 @@ static void parse_bindcmdaddress(char *);
static void parse_broadcast(char *);
static void parse_clientloglimit(char *);
static void parse_fallbackdrift(char *);
static void parse_hwtimestamp(char *);
static void parse_include(char *);
static void parse_initstepslew(char *);
static void parse_leapsecmode(char *);
@@ -78,7 +79,7 @@ static void parse_tempcomp(char *);
static int restarted = 0;
static char *rtc_device;
static int acquisition_port = -1;
static int ntp_port = 123;
static int ntp_port = NTP_PORT;
static char *keys_file = NULL;
static char *drift_file = NULL;
static char *rtc_file = NULL;
@@ -89,19 +90,20 @@ static double max_drift = 500000.0; /* in ppm */
static double max_slew_rate = 1e6 / 12.0; /* in ppm */
static double max_distance = 3.0;
static double max_jitter = 1.0;
static double reselect_distance = 1e-4;
static double stratum_weight = 1e-3;
static double combine_limit = 3.0;
static int cmd_port = DEFAULT_CANDM_PORT;
static int raw_measurements = 0;
static int do_log_measurements = 0;
static int do_log_statistics = 0;
static int do_log_tracking = 0;
static int do_log_rtc = 0;
static int do_log_refclocks = 0;
static int do_log_tempcomp = 0;
static int do_dump_on_exit = 0;
static int log_banner = 32;
static char *logdir;
static char *dumpdir;
@@ -147,7 +149,7 @@ static double max_offset;
/* Maximum and minimum number of samples per source */
static int max_samples = 0; /* no limit */
static int min_samples = 0;
static int min_samples = 6;
/* Threshold for a time adjustment to be logged to syslog */
static double log_change_threshold = 1.0;
@@ -181,6 +183,9 @@ static IPAddr bind_cmd_address4, bind_cmd_address6;
/* Path to the Unix domain command socket. */
static char *bind_cmd_path;
/* Path to Samba (ntp_signd) socket. */
static char *ntp_signd_socket = NULL;
/* Filename to use for storing pid of running chronyd, to prevent multiple
* chronyds being started. */
static char *pidfile;
@@ -189,10 +194,10 @@ static char *pidfile;
static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 3;
static int ntp_ratelimit_leak = 2;
static int cmd_ratelimit_enabled = 0;
static int cmd_ratelimit_interval = 1;
static int cmd_ratelimit_burst = 16;
static int cmd_ratelimit_interval = -4;
static int cmd_ratelimit_burst = 8;
static int cmd_ratelimit_leak = 2;
/* Smoothing constants */
@@ -218,6 +223,9 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
/* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces;
typedef struct {
NTP_Source_Type type;
int pool;
@@ -262,7 +270,7 @@ static const char *processed_command;
static void
command_parse_error(void)
{
LOG_FATAL(LOGF_Configure, "Could not parse %s directive at line %d%s%s",
LOG_FATAL("Could not parse %s directive at line %d%s%s",
processed_command, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
}
@@ -272,7 +280,7 @@ command_parse_error(void)
static void
other_parse_error(const char *message)
{
LOG_FATAL(LOGF_Configure, "%s at line %d%s%s",
LOG_FATAL("%s at line %d%s%s",
message, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
}
@@ -305,7 +313,7 @@ check_number_of_args(char *line, int num)
num -= get_number_of_args(line);
if (num) {
LOG_FATAL(LOGF_Configure, "%s arguments for %s directive at line %d%s%s",
LOG_FATAL("%s arguments for %s directive at line %d%s%s",
num > 0 ? "Missing" : "Too many",
processed_command, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : "");
@@ -315,10 +323,12 @@ check_number_of_args(char *line, int num)
/* ================================================== */
void
CNF_Initialise(int r)
CNF_Initialise(int r, int client_only)
{
restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
@@ -327,13 +337,20 @@ CNF_Initialise(int r)
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
dumpdir = Strdup(".");
logdir = Strdup(".");
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup("/var/run/chronyd.pid");
rtc_device = Strdup("/dev/rtc");
dumpdir = Strdup("");
logdir = Strdup("");
rtc_device = Strdup(DEFAULT_RTC_DEVICE);
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER);
if (client_only) {
cmd_port = ntp_port = 0;
bind_cmd_path = Strdup("");
pidfile = Strdup("");
} else {
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup(DEFAULT_PID_FILE);
}
}
/* ================================================== */
@@ -343,6 +360,10 @@ CNF_Finalise(void)
{
unsigned int i;
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
Free(((CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, i))->name);
ARR_DestroyInstance(hwts_interfaces);
for (i = 0; i < ARR_GetSize(ntp_sources); i++)
Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name);
@@ -361,6 +382,7 @@ CNF_Finalise(void)
Free(leapsec_tz);
Free(logdir);
Free(bind_cmd_path);
Free(ntp_signd_socket);
Free(pidfile);
Free(rtc_device);
Free(rtc_file);
@@ -382,12 +404,12 @@ CNF_ReadFile(const char *filename)
in = fopen(filename, "r");
if (!in) {
LOG_FATAL(LOGF_Configure, "Could not open configuration file %s : %s",
LOG_FATAL("Could not open configuration file %s : %s",
filename, strerror(errno));
return;
}
DEBUG_LOG(LOGF_Configure, "Reading %s", filename);
DEBUG_LOG("Reading %s", filename);
for (i = 1; fgets(line, sizeof(line), in); i++) {
CNF_ParseLine(filename, i, line);
@@ -453,11 +475,13 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "dumpdir")) {
parse_string(p, &dumpdir);
} else if (!strcasecmp(command, "dumponexit")) {
do_dump_on_exit = parse_null(p);
/* Silently ignored */
} else if (!strcasecmp(command, "fallbackdrift")) {
parse_fallbackdrift(p);
} else if (!strcasecmp(command, "hwclockfile")) {
parse_string(p, &hwclock_file);
} else if (!strcasecmp(command, "hwtimestamp")) {
parse_hwtimestamp(p);
} else if (!strcasecmp(command, "include")) {
parse_include(p);
} else if (!strcasecmp(command, "initstepslew")) {
@@ -494,6 +518,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_double(p, &max_distance);
} else if (!strcasecmp(command, "maxdrift")) {
parse_double(p, &max_drift);
} else if (!strcasecmp(command, "maxjitter")) {
parse_double(p, &max_jitter);
} else if (!strcasecmp(command, "maxsamples")) {
parse_int(p, &max_samples);
} else if (!strcasecmp(command, "maxslewrate")) {
@@ -506,6 +532,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_int(p, &min_sources);
} else if (!strcasecmp(command, "noclientlog")) {
no_client_log = parse_null(p);
} else if (!strcasecmp(command, "ntpsigndsocket")) {
parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "peer")) {
parse_source(p, NTP_PEER, 0);
} else if (!strcasecmp(command, "pidfile")) {
@@ -547,7 +575,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
!strcasecmp(command, "generatecommandkey") ||
!strcasecmp(command, "linux_freq_scale") ||
!strcasecmp(command, "linux_hz")) {
LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command);
LOG(LOGS_WARN, "%s directive is no longer supported", command);
} else {
other_parse_error("Invalid command");
}
@@ -604,17 +632,13 @@ parse_null(char *line)
static void
parse_source(char *line, NTP_Source_Type type, int pool)
{
CPS_Status status;
NTP_Source source;
char str[64];
source.type = type;
source.pool = pool;
status = CPS_ParseNTPSourceAdd(line, &source.params);
if (status != CPS_Success) {
CPS_StatusToString(status, str, sizeof (str));
other_parse_error(str);
if (!CPS_ParseNTPSourceAdd(line, &source.params)) {
command_parse_error();
return;
}
@@ -657,8 +681,9 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int max_lock_age, pps_forced;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
unsigned char ref[5];
RefclockParameters *refclock;
@@ -666,6 +691,7 @@ parse_refclock(char *line)
poll = 4;
dpoll = 0;
filter_length = 64;
pps_forced = 0;
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
max_samples = SRC_DEFAULT_MAXSAMPLES;
@@ -674,7 +700,9 @@ parse_refclock(char *line)
delay = 1e-9;
precision = 0.0;
max_dispersion = 0.0;
pulse_width = 0.0;
ref_id = 0;
max_lock_age = 2;
lock_ref_id = 0;
if (!*line) {
@@ -725,6 +753,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &min_samples, &n) != 1)
break;
} else if (!strcasecmp(cmd, "maxlockage")) {
if (sscanf(line, "%d%n", &max_lock_age, &n) != 1)
break;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &max_samples, &n) != 1)
break;
@@ -734,12 +765,18 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "delay")) {
if (sscanf(line, "%lf%n", &delay, &n) != 1)
break;
} else if (!strcasecmp(cmd, "pps")) {
n = 0;
pps_forced = 1;
} else if (!strcasecmp(cmd, "precision")) {
if (sscanf(line, "%lf%n", &precision, &n) != 1)
break;
} else if (!strcasecmp(cmd, "maxdispersion")) {
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
break;
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
} else if (!strcasecmp(cmd, "noselect")) {
n = 0;
sel_options |= SRC_SELECT_NOSELECT;
@@ -769,6 +806,7 @@ parse_refclock(char *line)
refclock->driver_poll = dpoll;
refclock->poll = poll;
refclock->filter_length = filter_length;
refclock->pps_forced = pps_forced;
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
refclock->max_samples = max_samples;
@@ -777,7 +815,9 @@ parse_refclock(char *line)
refclock->delay = delay;
refclock->precision = precision;
refclock->max_dispersion = max_dispersion;
refclock->pulse_width = pulse_width;
refclock->ref_id = ref_id;
refclock->max_lock_age = max_lock_age;
refclock->lock_ref_id = lock_ref_id;
}
@@ -791,7 +831,10 @@ parse_log(char *line)
log_name = line;
line = CPS_SplitWord(line);
if (*log_name) {
if (!strcmp(log_name, "measurements")) {
if (!strcmp(log_name, "rawmeasurements")) {
do_log_measurements = 1;
raw_measurements = 1;
} else if (!strcmp(log_name, "measurements")) {
do_log_measurements = 1;
} else if (!strcmp(log_name, "statistics")) {
do_log_statistics = 1;
@@ -851,7 +894,7 @@ parse_initstepslew(char *line)
if (DNS_Name2IPAddress(hostname, &ip_addr, 1) == DNS_Success) {
ARR_AppendElement(init_sources, &ip_addr);
} else {
LOG(LOGS_WARN, LOGF_Configure, "Could not resolve address of initstepslew server %s", hostname);
LOG(LOGS_WARN, "Could not resolve address of initstepslew server %s", hostname);
}
}
}
@@ -1028,7 +1071,7 @@ parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
}
} else {
if (DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
if (!slashpos && DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
new_node->allow = allow;
new_node->all = all;
@@ -1143,7 +1186,7 @@ parse_broadcast(char *line)
}
} else {
/* default port */
port = 123;
port = NTP_PORT;
}
destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts);
@@ -1219,16 +1262,84 @@ parse_tempcomp(char *line)
/* ================================================== */
static void
parse_hwtimestamp(char *line)
{
CNF_HwTsInterface *iface;
char *p, filter[5];
int n;
if (!*line) {
command_parse_error();
return;
}
p = line;
line = CPS_SplitWord(line);
iface = ARR_GetNewElement(hwts_interfaces);
iface->name = Strdup(p);
iface->minpoll = 0;
iface->nocrossts = 0;
iface->rxfilter = CNF_HWTS_RXFILTER_NTP;
iface->precision = 100.0e-9;
iface->tx_comp = 0.0;
iface->rx_comp = 0.0;
for (p = line; *p; line += n, p = line) {
line = CPS_SplitWord(line);
if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
} else if (!strcasecmp(p, "precision")) {
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
break;
} else if (!strcasecmp(p, "rxcomp")) {
if (sscanf(line, "%lf%n", &iface->rx_comp, &n) != 1)
break;
} else if (!strcasecmp(p, "txcomp")) {
if (sscanf(line, "%lf%n", &iface->tx_comp, &n) != 1)
break;
} else if (!strcasecmp(p, "rxfilter")) {
if (sscanf(line, "%4s%n", filter, &n) != 1)
break;
if (!strcasecmp(filter, "none"))
iface->rxfilter = CNF_HWTS_RXFILTER_NONE;
else if (!strcasecmp(filter, "ntp"))
iface->rxfilter = CNF_HWTS_RXFILTER_NTP;
else if (!strcasecmp(filter, "all"))
iface->rxfilter = CNF_HWTS_RXFILTER_ALL;
else
break;
} else if (!strcasecmp(p, "nocrossts")) {
n = 0;
iface->nocrossts = 1;
} else {
break;
}
}
if (*p)
command_parse_error();
}
/* ================================================== */
static void
parse_include(char *line)
{
glob_t gl;
size_t i;
int r;
check_number_of_args(line, 1);
if (glob(line, 0, NULL, &gl)) {
DEBUG_LOG(LOGF_Configure, "glob of %s failed", line);
if ((r = glob(line, GLOB_ERR | GLOB_NOMAGIC, NULL, &gl)) != 0) {
if (r != GLOB_NOMATCH)
LOG_FATAL("Could not search for files matching %s", line);
DEBUG_LOG("glob of %s failed", line);
return;
}
@@ -1245,9 +1356,6 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
{
char *dir;
UTI_CreateDirAndParents(logdir, 0755, uid, gid);
UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
/* Create a directory for the Unix domain command socket */
if (bind_cmd_path[0]) {
dir = UTI_PathToDir(bind_cmd_path);
@@ -1257,12 +1365,17 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
existed. It MUST NOT be accessible by others as permissions on Unix
domain sockets are ignored on some systems (e.g. Solaris). */
if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) {
LOG(LOGS_WARN, LOGF_Configure, "Disabled command socket %s", bind_cmd_path);
LOG(LOGS_WARN, "Disabled command socket %s", bind_cmd_path);
bind_cmd_path[0] = '\0';
}
Free(dir);
}
if (logdir[0])
UTI_CreateDirAndParents(logdir, 0755, uid, gid);
if (dumpdir[0])
UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
}
/* ================================================== */
@@ -1391,8 +1504,9 @@ CNF_GetDumpDir(void)
/* ================================================== */
int
CNF_GetLogMeasurements(void)
CNF_GetLogMeasurements(int *raw)
{
*raw = raw_measurements;
return do_log_measurements;
}
@@ -1470,14 +1584,6 @@ CNF_GetRtcDevice(void)
/* ================================================== */
int
CNF_GetDumpOnExit(void)
{
return do_dump_on_exit;
}
/* ================================================== */
double
CNF_GetMaxUpdateSkew(void)
{
@@ -1526,6 +1632,14 @@ CNF_GetMaxDistance(void)
/* ================================================== */
double
CNF_GetMaxJitter(void)
{
return max_jitter;
}
/* ================================================== */
double
CNF_GetReselectDistance(void)
{
@@ -1650,7 +1764,7 @@ CNF_SetupAccessRestrictions(void)
node = ARR_GetElement(ntp_restrictions, i);
status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
if (!status) {
LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
}
}
@@ -1658,7 +1772,7 @@ CNF_SetupAccessRestrictions(void)
node = ARR_GetElement(cmd_restrictions, i);
status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
if (!status) {
LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
}
}
@@ -1740,6 +1854,14 @@ CNF_GetBindCommandAddress(int family, IPAddr *addr)
/* ================================================== */
char *
CNF_GetNtpSigndSocket(void)
{
return ntp_signd_socket;
}
/* ================================================== */
char *
CNF_GetPidFile(void)
{
@@ -1877,3 +1999,15 @@ CNF_GetInitStepThreshold(void)
{
return init_slew_threshold;
}
/* ================================================== */
int
CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
{
if (index >= ARR_GetSize(hwts_interfaces))
return 0;
*iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index);
return 1;
}

23
conf.h
View File

@@ -31,7 +31,7 @@
#include "addressing.h"
#include "reference.h"
extern void CNF_Initialise(int restarted);
extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void);
extern char *CNF_GetRtcDevice(void);
@@ -52,7 +52,7 @@ extern char *CNF_GetDriftFile(void);
extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(void);
extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
@@ -60,7 +60,6 @@ extern int CNF_GetLogRefclocks(void);
extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void);
extern int CNF_GetDumpOnExit(void);
extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void);
extern int CNF_GetRtcOnUtc(void);
@@ -76,6 +75,7 @@ extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindCommandPath(void);
extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
@@ -88,6 +88,7 @@ extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
@@ -117,4 +118,20 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
#define CNF_HWTS_RXFILTER_NONE 0
#define CNF_HWTS_RXFILTER_NTP 1
#define CNF_HWTS_RXFILTER_ALL 2
typedef struct {
char *name;
int minpoll;
int nocrossts;
int rxfilter;
double precision;
double tx_comp;
double rx_comp;
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
#endif /* GOT_CONF_H */

200
configure vendored
View File

@@ -4,7 +4,8 @@
# chronyd/chronyc - Programs for keeping computer clocks accurate.
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Miroslav Lichvar 2009, 2012-2015
# Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2016
#
# =======================================================================
@@ -24,6 +25,7 @@ test_code () {
printf "%s" "Checking for $name : "
(
echo "#include \"config.h\""
for h in $headers; do
echo "#include <$h>"
done
@@ -79,9 +81,8 @@ For better control, use the options below.
--disable-readline Disable line editing support
--without-readline Don't use GNU readline even if it is available
--without-editline Don't use editline even if it is available
--readline-dir=DIR Specify parent of readline include and lib directories
--readline-inc-dir=DIR Specify where readline include directory is
--readline-lib-dir=DIR Specify where readline lib directory is
--with-readline-includes=DIR Specify where readline include directory is
--with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5
--without-nss Don't use NSS even if it is available
@@ -99,10 +100,15 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support
@@ -114,7 +120,7 @@ Fine tuning of the installation directories:
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/chrony]
--localstatedir=DIR modifiable single-machine data [/var]
--chronysockdir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony]
--chronyrundir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony]
--chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony]
Overriding system detection when cross-compiling:
@@ -213,9 +219,16 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_clock_gettime=1
try_recvmmsg=1
feat_timestamping=1
try_timestamping=0
feat_ntp_signd=0
ntp_era_split=""
default_user="root"
default_hwclockfile=""
default_pidfile="/var/run/chronyd.pid"
default_rtcdevice="/dev/rtc"
mail_program="/usr/lib/sendmail"
for option
@@ -269,8 +282,8 @@ do
--localstatedir=* )
SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'`
;;
--chronysockdir=* )
SETCHRONYSOCKDIR=`echo $option | sed -e 's/^.*=//;'`
--chronyrundir=* | --chronysockdir=* )
SETCHRONYRUNDIR=`echo $option | sed -e 's/^.*=//;'`
;;
--chronyvardir=* )
SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'`
@@ -317,6 +330,15 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
--without-clock-gettime)
try_clock_gettime=0
;;
--disable-timestamping)
feat_timestamping=0
;;
--enable-ntp-signd)
feat_ntp_signd=1
;;
--with-ntp-era=* )
ntp_era_split=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -326,6 +348,12 @@ do
--with-hwclockfile=* )
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
;;
--with-pidfile=* )
default_pidfile=`echo $option | sed -e 's/^.*=//;'`
;;
--with-rtcdevice=* )
default_rtcdevice=`echo $option | sed -e 's/^.*=//;'`
;;
--with-sendmail=* )
mail_program=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -366,6 +394,7 @@ case $OPERATINGSYSTEM in
[ $try_libcap != "0" ] && try_libcap=1
try_rtc=1
[ $try_seccomp != "0" ] && try_seccomp=1
try_timestamping=1
try_setsched=1
try_lockmem=1
try_phc=1
@@ -373,6 +402,9 @@ case $OPERATINGSYSTEM in
echo "Configuring for " $SYSTEM
;;
FreeBSD)
# recvmmsg() seems to be broken on FreeBSD 11.0 and it's just
# a wrapper around recvmsg()
try_recvmmsg=0
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
add_def FREEBSD
if [ $feat_droproot = "1" ]; then
@@ -396,7 +428,16 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
fi
echo "Configuring for MacOS X (" $SYSTEM "MacOS X version" $VERSION ")"
major=`echo $VERSION | cut -d. -f1`
# ntp_adjtime is not available in macOS 10.12 (Darwin 16.x.x) and earlier
if [ $major -gt "16" ]; then
add_def HAVE_MACOS_SYS_TIMEX
EXTRA_OBJECTS="$EXTRA_OBJECTS sys_generic.o sys_netbsd.o sys_timex.o"
if [ $feat_droproot = "1" ]; then
priv_ops="$priv_ops ADJUSTTIMEX"
fi
fi
echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
@@ -432,8 +473,13 @@ fi
if [ $feat_ntp = "1" ]; then
add_def FEAT_NTP
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o"
if [ $feat_ntp_signd = "1" ]; then
add_def FEAT_SIGND
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
fi
else
feat_asyncdns=0
feat_timestamping=0
fi
if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then
@@ -453,14 +499,16 @@ MYCPPFLAGS="$CPPFLAGS"
MYLDFLAGS="$LDFLAGS"
if [ "x$MYCC" = "x" ]; then
MYCC=gcc
if ! test_code "$MYCC" '' '' '' ''; then
MYCC=cc
if ! test_code "$MYCC" '' '' '' ''; then
for cc in gcc clang cc ""; do
if [ "x$cc" = "x" ]; then
echo "error: no C compiler found"
exit 1
fi
fi
MYCC=$cc
if test_code "$MYCC" '' '' '' ''; then
break
fi
done
else
if ! test_code "$MYCC" '' '' '' ''; then
echo "error: C compiler $MYCC cannot create executables"
@@ -470,9 +518,25 @@ fi
if [ "x$MYCFLAGS" = "x" ]; then
MYCFLAGS="-O2 -g"
TESTCFLAGS="-D_FORTIFY_SOURCE=2 -fPIE"
TESTLDFLAGS="-pie -Wl,-z,relro,-z,now"
if test_code 'hardening compiler options' '' "$TESTCFLAGS" "$TESTLDFLAGS" ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
MYLDFLAGS="$MYLDFLAGS $TESTLDFLAGS"
fi
TESTCFLAGS="-fstack-protector-strong --param=ssp-buffer-size=4"
if test_code '-fstack-protector-strong' '' "$TESTCFLAGS" '' ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
else
TESTCFLAGS="-fstack-protector --param=ssp-buffer-size=4"
if test_code '-fstack-protector' '' "$TESTCFLAGS" '' ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
fi
fi
fi
if [ "x$MYCC" = "xgcc" ]; then
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
@@ -559,6 +623,21 @@ then
fi
fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
fi
fi
fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
'return getaddrinfo(0, 0, 0, 0);'
then
@@ -579,6 +658,51 @@ if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; the
add_def HAVE_ARC4RANDOM
fi
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then
add_def HAVE_GETRANDOM
fi
RECVMMSG_CODE='
struct mmsghdr hdr;
return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
if [ $try_recvmmsg = "1" ]; then
if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
add_def HAVE_RECVMMSG
else
if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
"$EXTRA_LIBS" "$RECVMMSG_CODE"
then
add_def _GNU_SOURCE
add_def HAVE_RECVMMSG
fi
fi
fi
if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
test_code 'SW/HW timestamping' 'sys/types.h sys/socket.h linux/net_tstamp.h
linux/errqueue.h linux/ptp_clock.h' '' '' '
int val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG;
return sizeof (struct scm_timestamping) + SCM_TSTAMP_SND + PTP_SYS_OFFSET +
setsockopt(0, SOL_SOCKET, SO_SELECT_ERR_QUEUE + SO_TIMESTAMPING,
&val, sizeof (val));'
then
add_def HAVE_LINUX_TIMESTAMPING
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o"
if test_code 'other timestamping options' \
'sys/types.h sys/socket.h linux/net_tstamp.h' '' '' '
struct scm_ts_pktinfo pktinfo;
pktinfo.if_index = pktinfo.pkt_length = 0;
return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then
add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW 1
fi
fi
timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
@@ -628,7 +752,7 @@ then
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper
# process works on one request at the time and the async resolver could
# block the main thread
priv_ops="NAME2IPADDRESS"
priv_ops="NAME2IPADDRESS RELOADDNS"
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi
@@ -649,19 +773,13 @@ then
fi
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
'ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);'
then
if test_code 'clock_gettime()' 'time.h' '' '' 'clock_gettime(0, NULL);'; then
add_def FEAT_PHC
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(0, NULL);'
then
EXTRA_LIBS="$EXTRA_LIBS -lrt"
add_def FEAT_PHC
fi
fi
grep 'HAVE_LINUX_TIMESTAMPING' config.h > /dev/null ||
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o"
add_def FEAT_PHC
fi
if [ $try_setsched = "1" ] && \
@@ -691,7 +809,6 @@ then
add_def FORCE_DNSRETRY
fi
READLINE_COMPILE=""
READLINE_LINK=""
if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then
@@ -701,7 +818,7 @@ if [ $feat_readline = "1" ]; then
then
add_def FEAT_READLINE
add_def USE_EDITLINE
READLINE_COMPILE="$readline_inc"
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -ledit"
fi
fi
@@ -712,7 +829,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
READLINE_COMPILE="$readline_inc"
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -lreadline"
fi
fi
@@ -724,7 +841,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
READLINE_COMPILE="$readline_inc"
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi
fi
@@ -733,7 +850,6 @@ if [ $feat_readline = "1" ]; then
fi
HASH_OBJ="hash_intmd5.o"
HASH_COMPILE=""
HASH_LINK=""
if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
@@ -744,9 +860,9 @@ if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
then
HASH_OBJ="hash_nss.o"
HASH_COMPILE="$test_cflags"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
fi
fi
@@ -756,9 +872,9 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
then
HASH_OBJ="hash_tomcrypt.o"
HASH_COMPILE="-I/usr/include/tomcrypt"
HASH_LINK="-ltomcrypt"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/tomcrypt"
add_def FEAT_SECHASH
fi
fi
@@ -808,9 +924,9 @@ if [ "x$SETLOCALSTATEDIR" != "x" ]; then
LOCALSTATEDIR=$SETLOCALSTATEDIR
fi
CHRONYSOCKDIR=${LOCALSTATEDIR}/run/chrony
if [ "x$SETCHRONYSOCKDIR" != "x" ]; then
CHRONYSOCKDIR=$SETCHRONYSOCKDIR
CHRONYRUNDIR=${LOCALSTATEDIR}/run/chrony
if [ "x$SETCHRONYRUNDIR" != "x" ]; then
CHRONYRUNDIR=$SETCHRONYRUNDIR
fi
CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony
@@ -820,13 +936,15 @@ fi
add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\""
add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
add_def DEFAULT_PID_FILE "\"$default_pidfile\""
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYSOCKDIR/chronyd.sock\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH ASYNCDNS`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH SIGND ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
@@ -850,18 +968,18 @@ do
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
s%@HASH_OBJ@%${HASH_OBJ}%;\
s%@HASH_COMPILE@%${HASH_COMPILE}%;\
s%@SYSCONFDIR@%${SYSCONFDIR}%;\
s%@BINDIR@%${BINDIR}%;\
s%@SBINDIR@%${SBINDIR}%;\
s%@DOCDIR@%${DOCDIR}%;\
s%@MANDIR@%${MANDIR}%;\
s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\
s%@CHRONYSOCKDIR@%${CHRONYSOCKDIR}%;\
s%@CHRONYRUNDIR@%${CHRONYRUNDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
s%@DEFAULT_USER@%${default_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f

View File

@@ -1,16 +1,16 @@
Notes for installing chrony on MacOS X
Notes for installing chrony on macOS
Author: Bryan Christianson (bryan@whatroute.net)
------------------------------------------------
These files are for those admins/users who would prefer to install chrony
from the source distribution and are intended as guidelines rather than
being definitive. They can be edited with a plain text editor, such as
vi, emacs or your favourite IDE (xcode)
vi, emacs or your favourite IDE (Xcode)
It is assumed you are comfortable with installing software from the
terminal command line and know how to use sudo to acquire root access.
If you are not familiar with the MacOS X command line then
If you are not familiar with the macOS command line then
please consider using ChronyControl from http://whatroute.net/chronycontrol.html
ChronyControl provides a gui wrapper for installing these files and sets the
@@ -72,7 +72,7 @@ Installing the support files
1. chronylogrotate.sh
This is a simple shell script that deletes old log files. Unfortunately because
of the need to run chronyc, the standard MacOS X logrotation does not work with
of the need to run chronyc, the standard macOS logrotation does not work with
chrony logs.
This script runs on a daily basis under control of launchd and should be

View File

@@ -13,19 +13,23 @@ BINDIR = @BINDIR@
SBINDIR = @SBINDIR@
MANDIR = @MANDIR@
DOCDIR = @DOCDIR@
CHRONYSOCKDIR = @CHRONYSOCKDIR@
CHRONYRUNDIR = @CHRONYRUNDIR@
CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
s%\@BINDIR\@%$(BINDIR)%g;\
s%\@SBINDIR\@%$(SBINDIR)%g;\
s%\@CHRONY_VERSION\@%$(CHRONY_VERSION)%g;\
s%\@DEFAULT_HWCLOCK_FILE\@%$(DEFAULT_HWCLOCK_FILE)%g;\
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
s%\@CHRONYSOCKDIR\@%$(CHRONYSOCKDIR)%g;\
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
man: $(MAN_FILES) $(MAN_IN_FILES)

View File

@@ -1,7 +1,8 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2016
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
@@ -67,11 +68,15 @@ options:
Although *chronyd* will trim the rate at which it samples the server during
normal operation, the user might want to constrain the minimum polling interval.
This is always defined as a power of 2, so *minpoll 5* would mean that the
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds).
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds),
the minimum is -4 (1/16th of a second), and the maximum is 24 (6 months). Note
that intervals shorter than 6 (64 seconds) should generally not be used with
public servers on the Internet, because it might be considered abuse.
*maxpoll* _poll_:::
In a similar way, the user might want to constrain the maximum polling interval.
Again this is specified as a power of 2, *maxpoll 9* indicates that the polling
interval must stay at or below 512 seconds. The default is 10 (1024 seconds).
interval must stay at or below 512 seconds. The default is 10 (1024 seconds),
the minimum is 0 (1 second), and the maximum is 24 (6 months).
*iburst*:::
If this option is set, the interval between the first four polls will be 2
seconds instead of _minpoll_. This is useful to quickly get the first update of
@@ -105,17 +110,25 @@ If the user knows that round trip delays above a certain level should cause the
measurement to be ignored, this level can be defined with the *maxdelay*
option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is
3 seconds.
3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_:::
This option is similar to the maxdelay option above. *chronyd* keeps a record
of the minimum round-trip delay amongst the previous measurements that it has
buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected.
maxdelayratio times the minimum delay, it will be rejected. This option works
only in the *server* directive when not in the interleaved mode.
*maxdelaydevratio* _ratio_:::
If a measurement has a ratio of the increase in the round-trip delay from the
minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0.
*offset* _offset_:::
This option specifies a correction (in seconds) which will be applied to
offsets measured with this source. It's particularly useful to compensate for a
known asymmetry in network delay or timestamping errors. For example, if
packets sent to the source were on average delayed by 100 microseconds more
than packets sent from the source back, the correction would be -0.00005 (-50
microseconds). The default is 0.0.
*minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
@@ -149,11 +162,31 @@ clock. Together with the *trust* option this might be useful to allow a trusted
authenticated source to be safely combined with unauthenticated sources in
order to improve the accuracy of the clock. They can be selected and used for
synchronisation only if they agree with the trusted and required source.
*xleave*:::
This option enables an interleaved mode which allows the server or the peer to
send transmit timestamps captured after the actual transmission (e.g. when the
server or the peer is running *chronyd* with software (kernel) or hardware
timestamping). This can significantly improve the accuracy of the measurements.
+
The interleaved mode is compatible with servers that support only the basic
mode, but peers must both support and have enabled the interleaved mode,
otherwise the synchronisation will work only in one direction. Note that even
servers that support the interleaved mode might respond in the basic mode as
the interleaved mode requires the servers to keep some state for each client
and the state might be dropped when there are too many clients (e.g.
<<clientloglimit,*clientloglimit*>> is too small), or it might be overwritten
by other clients that have the same IP address (e.g. computers behind NAT or
someone sending requests with a spoofed source address).
+
With longer polling intervals, it is recommended to combine the *xleave* option
with the *presend* option in order to shorten the interval in which the server
has to keep the state to be able to respond in the interleaved mode. The
shorter interval also improves accuracy of the measured offset and delay.
*polltarget* _target_:::
Target number of measurements to use for the regression algorithm which
*chronyd* will try to maintain by adjusting the polling interval between
*minpoll* and *maxpoll*. A higher target makes *chronyd* prefer shorter polling
intervals. The default is 6 and a useful range is from 6 to 60.
intervals. The default is 8 and a useful range is from 6 to 60.
*port* _port_:::
This option allows the UDP port on which the server understands NTP requests to
be specified. For normal servers this option should not be required (the
@@ -177,8 +210,11 @@ presend 9
----
+
when the polling interval is 512 seconds or more, an extra NTP client packet
will be sent to the server a short time (4 seconds) before making the actual
will be sent to the server a short time (2 seconds) before making the actual
measurement.
+
The *presend* option cannot be used in the *peer* directive. If it is used
with the *xleave* option, *chronyd* will send two extra packets instead of one.
*minstratum* _stratum_:::
When the synchronisation source is selected from available sources, sources
with lower stratum are normally slightly preferred. This option can be used to
@@ -187,9 +223,13 @@ avoid selecting that source. This is useful with low stratum sources that are
known to be unreliable or inaccurate and which should be used only when other
sources are unreachable.
*version* _version_:::
This option sets the NTP version number used in packets sent to the server.
This can be useful when the server runs an old NTP implementation that does not
respond to newer versions. The default version number is 4.
This option sets the NTP version of packets sent to the server. This can be
useful when the server runs an old NTP implementation that does not respond to
requests using a newer version. The default version depends on whether a key is
specified by the *key* option and which authentication hash function the key
is using. If the output size of the hash function is longer than 160 bits, the
default version is 3 for compatibility with older *chronyd* servers. Otherwise,
the default version is 4.
[[pool]]*pool* _name_ [_option_]...::
The syntax of this directive is similar to that for the <<server,*server*>>
@@ -218,26 +258,33 @@ pool pool.ntp.org iburst maxsources 3
[[peer]]*peer* _hostname_ [_option_]...::
The syntax of this directive is identical to that for the <<server,*server*>>
directive, except that it is used to specify an NTP peer rather than an NTP
server.
directive, except that it specifies a symmetric association with an NTP peer
instead of a client/server association with an NTP server. A single symmetric
association allows the peers to be both servers and clients to each other. This
is mainly useful when the NTP implementation of the peer (e.g. *ntpd*) supports
ephemeral symmetric associations and does not need to be configured with an
address of this host. *chronyd* does not support ephemeral associations.
+
When a key is specified by the *key* option to enable authentication, both
peers must be configured to use the same key and the same key number.
peers must use the same key and the same key number.
+
Please note that NTP peers that are not configured with a key to enable
authentication are vulnerable to a denial-of-service attack. An attacker
knowing that NTP hosts A and B are peering with each other can send a packet
with random timestamps to host A with source address of B which will set the
NTP state variables on A to the values sent by the attacker. Host A will then
send on its next poll to B a packet with an origin timestamp that does not match
the transmit timestamp of B and the packet will be dropped. If the attacker
does this periodically for both hosts, they will not be able to synchronise to
each other.
+
This attack can be prevented by enabling authentication with the *key* option,
or by using the <<server,*server*>> directive on both sides to specify the other
host as a server instead of a peer. The disadvantage of the *server* directive
is that it will double the network traffic between the two hosts.
Note that the symmetric mode is less secure than the client/server mode. A
denial-of-service attack is possible on unauthenticated symmetric associations,
i.e. when the peer was specified without the *key* option. An attacker who does
not see network traffic between two hosts, but knows that they are peering with
each other, can periodically send them unauthenticated packets with spoofed
source addresses in order to disrupt their NTP state and prevent them from
synchronising to each other. When the association is authenticated, an attacker
who does see the network traffic, but cannot prevent the packets from reaching
the other host, can still disrupt the state by replaying old packets. The
attacker has effectively the same power as a man-in-the-middle attacker. A
partial protection against this attack is implemented in *chronyd*, which can
protect the peers if they are using the same polling interval and they never
sent an authenticated packet with a timestamp from future, but it should not be
relied on as it is difficult to ensure the conditions are met. If two hosts
should be able to synchronise to each other in both directions, it is
recommended to use two separate client/server associations (specified by the
<<server,*server*>> directive on both hosts) instead.
[[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...::
In normal operation, *chronyd* slews the time when it needs to adjust the
@@ -297,47 +344,56 @@ for *initstepslew* to finish before exiting. This is useful to prevent programs
started in the boot sequence after *chronyd* from reading the clock before it
has been stepped.
[[refclock]]*refclock* _driver_ _parameter_ [_option_]...::
[[refclock]]*refclock* _driver_ _parameter_[:__option__,...] [_option_]...::
The *refclock* directive specifies a hardware reference clock to be used as a
time source. It has two mandatory parameters, a driver name and a
driver-specific parameter.
driver-specific parameter. The two parameters are followed by zero or more
refclock options. Some drivers have special options, which can be appended to
the driver-specific parameter (separated by the *:* and *,* characters).
+
There are currently four drivers included:
There are four drivers included in *chronyd*:
+
*PPS*:::
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
the PPS device (typically _/dev/pps?_). The assert events are used for
synchronisation by default. String *:clear* can be appended to the path to use
the clear events instead.
the PPS device (typically _/dev/pps?_). As PPS refclocks do not supply full
time, another time source (e.g. NTP server or non-PPS refclock) is needed to
complete samples from the PPS refclock. An alternative is to enable the
<<local,*local*>> directive to allow synchronisation with some unknown but
constant offset. The driver supports the following option:
+
As PPS refclocks don't supply full time, *chronyd* needs to be configured with
another time source (NTP or non-PPS refclock) in order to complete samples from
the PPS refclock. An alternative is to enable the <<local,*local*>> directive
to allow synchronisation with some unknown but constant offset.
For example:
*clear*::::
By default, the PPS refclock uses assert events (rising edge) for
synchronisation. With this option, it will use clear events (falling edge)
instead.
+
:::
Examples:
+
----
refclock PPS /dev/pps0 lock NMEA
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
----
+
*SHM*:::
NTP shared memory driver. This driver uses a shared memory segment to receive
samples from another process. The parameter is the number of the shared memory
segment, typically 0, 1, 2 or 3. For example:
samples from another process (e.g. *gpsd*). The parameter is the number of the
shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
supports the following option:
+
*perm*=_mode_::::
This option specifies the permissions of the shared memory segment created by
*chronyd*. They are specified as a numeric mode. The default value is 0600
(read-write access for owner only).
:::
+
Examples:
+
----
refclock SHM 1 poll 3 refid GPS1
refclock SHM 0 poll 3 refid GPS1
refclock SHM 1:perm=0644 refid GPS2
----
+
A driver option in form of *:perm=NNN* can be appended to the segment number to
create the segment with permissions other than the default 0600.
+
Examples of applications that can be used as SHM refclocks are
http://www.catb.org/gpsd/[*gpsd*],
http://www.buzzard.me.uk/jonathan/radioclock.html[*radioclk*], and
https://www.vanheusden.com/time/omnisync/[*omnisync*].
+
*SOCK*:::
Unix domain socket driver. It is similar to the SHM driver, but samples are
received from a Unix domain socket instead of shared memory and the messages
@@ -357,17 +413,44 @@ refclock SOCK /var/run/chrony.ttyS0.sock
+
*PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of
the PTP clock, which for example can be synchronised by *ptp4l* from
http://linuxptp.sourceforge.net[*linuxptp*]. PTP clocks are typically kept in
TAI instead of UTC, so the *offset* option should be used to compensate for the
current UTC-TAI offset. For example:
the PTP clock which should be used as a time source. If the clock is kept in
TAI instead of UTC (e.g. it is synchronised by a PTP daemon), the current
UTC-TAI offset needs to be specified by the *offset* option. Alternatively, the
*pps* refclock option can be enabled to treat the PHC as a PPS refclock, using
only the sub-second offset for synchronisation. The driver supports the
following options:
+
*nocrossts*::::
This option disables use of precise cross timestamping.
*extpps*::::
This option enables a PPS mode in which the PTP clock is timestamping pulses
of an external PPS signal connected to the clock. The clock does not need to be
synchronised, but another time source is needed to complete the PPS samples.
Note that some PTP clocks cannot be configured to timestamp only assert or
clear events, and it is necessary to use the *width* option to filter wrong
PPS samples.
*pin*=_index_::::
This option specifies the index of the pin to which is connected the PPS
signal. The default value is 0.
*channel*=_index_::::
This option specifies the index of the channel for the PPS mode. The default
value is 0.
*clear*::::
This option enables timestamping of clear events (falling edge) instead of
assert events (rising edge) in the PPS mode. This may not work with some
clocks.
:::
+
Examples:
+
----
refclock PHC /dev/ptp0 poll 3 dpoll -2 offset -36
refclock PHC /dev/ptp0 poll 0 dpoll -2 offset -37
refclock PHC /dev/ptp1:nocrossts poll 3 pps
refclock PHC /dev/ptp2:extpps,pin=1 width 0.2 poll 2
----
+
::
The *refclock* directive also supports a number of options:
The *refclock* directive supports the following options:
+
*poll* _poll_:::
Timestamps produced by refclock drivers are not used immediately, but they are
@@ -395,6 +478,22 @@ This option sets the rate of the pulses in the PPS signal (in Hz). This option
controls how the pulses will be completed with real time. To actually receive
more than one pulse per second, a negative *dpoll* has to be specified (-3 for
a 5Hz signal). The default is 1.
*maxlockage* _pulses_:::
This option specifies in number of pulses how old can be samples from the
refclock specified by the *lock* option to be paired with the pulses.
Increasing this value is useful when the samples are produced at a lower rate
than the pulses. The default is 2.
*width* _width_:::
This option specifies the width of the pulses (in seconds). It is used to
filter PPS samples when the driver provides samples for both rising and falling
edges. Note that it reduces the maximum allowed error of the time source which
completes the PPS samples. If the duty cycle is configurable, 50% should be
prefered in order to maximise the allowed error.
*pps*:::
This options forces *chronyd* to treat any refclock (e.g. SHM or PHC) as a PPS
refclock. This can be useful when the refclock provides time with a variable
offset of a whole number of seconds (e.g. it uses TAI instead of UTC). Another
time source is needed to complete samples from the refclock.
*offset* _offset_:::
This option can be used to compensate for a constant error. The specified
offset (in seconds) is applied to all samples produced by the reference clock.
@@ -406,9 +505,8 @@ algorithm. Increasing the delay is useful to avoid having no majority in the
source selection or to make it prefer other sources. The default is 1e-9 (1
nanosecond).
*precision* _precision_:::
This option sets the refclock precision (in seconds). The default is 1e-6 (1
microsecond) for SHM refclock, and 1e-9 (1 nanosecond) for SOCK, PPS and PHC
refclocks.
This option sets the precision of the reference clock (in seconds). The default
value is the estimated precision of the system clock.
*maxdispersion* _dispersion_:::
Maximum allowed dispersion for filtered samples (in seconds). Samples with
larger estimated dispersion are ignored. By default, this limit is disabled.
@@ -489,34 +587,28 @@ can be specified.
To compute the rate of gain or loss of time, *chronyd* has to store a
measurement history for each of the time sources it uses.
+
Certain systems (Linux, FreeBSD, NetBSD, Solaris) have operating system support
for setting the rate of gain or loss to compensate for known errors. (On Mac OS
X, *chronyd* must simulate such a capability by periodically slewing the system
clock forwards or backwards by a suitable amount to compensate for the error
built up since the previous slew.)
All supported systems, with the exception of macOS 10.12 and earlier, have
operating system support for setting the rate of gain or loss to compensate for
known errors.
(On macOS 10.12 and earlier, *chronyd* must simulate such a capability by
periodically slewing the system clock forwards or backwards by a suitable amount
to compensate for the error built up since the previous slew.)
+
For such systems, it is possible to save the measurement history across
restarts of *chronyd* (assuming no changes are made to the system clock
behaviour whilst it is not running). If this capability is to be used (via the
*dumponexit* directive in the configuration file, or the
<<chronyc.adoc#dump,*dump*>> command in *chronyc*), the *dumpdir* directive
should be used to define the directory where the measurement histories are
saved.
behaviour whilst it is not running). The *dumpdir* directive defines the
directory where the measurement histories are saved when *chronyd* exits,
or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
+
An example of the directive is:
+
----
dumpdir @CHRONYVARDIR@
dumpdir @CHRONYRUNDIR@
----
+
A source whose reference ID (the IP address for IPv4 sources) is _1.2.3.4_
would have its measurement history saved in the file
_/var/lib/chrony/1.2.3.4.dat_.
[[dumponexit]]*dumponexit*::
If this directive is present, it indicates that *chronyd* should save the
measurement history for each of its time sources recorded whenever the program
exits. (See the <<dumpdir,*dumpdir*>> directive above.)
A source whose IP address is _1.2.3.4_ would have its measurement history saved
in the file _@CHRONYRUNDIR@/1.2.3.4.dat_. History of reference clocks is saved
to files named by their reference ID in form of _refid:XXXXXXXX.dat_.
[[maxsamples]]*maxsamples* _samples_::
The *maxsamples* directive sets the default maximum number of samples that
@@ -529,7 +621,7 @@ useful range is 4 to 64.
The *minsamples* directive sets the default minimum number of samples that
*chronyd* should keep for each source. This setting can be overridden for
individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
directives. The default value is 0. The useful range is 4 to 64.
directives. The default value is 6. The useful range is 4 to 64.
=== Source selection
@@ -561,6 +653,13 @@ Setting *maxdistance* to a larger value can be useful to allow synchronisation
with a server that only has a very infrequent connection to its sources and can
accumulate a large dispersion between updates of its clock.
[[maxjitter]]*maxjitter* _jitter_::
The *maxjitter* directive sets the maximum allowed jitter of the sources to not
be rejected by the source selection algorithm. This prevents synchronisation
with sources that have a small root distance, but their time is too variable.
+
By default, the maximum jitter is 1 second.
[[minsources]]*minsources* _sources_::
The *minsources* directive sets the minimum number of sources that need to be
considered as selectable in the source selection algorithm before the local
@@ -592,8 +691,9 @@ distances are in milliseconds.
[[corrtimeratio]]*corrtimeratio* _ratio_::
When *chronyd* is slewing the system clock to correct an offset, the rate at
which it is slewing adds to the frequency error of the clock. On Linux,
FreeBSD, NetBSD and Solaris this rate can be controlled.
which it is slewing adds to the frequency error of the clock. On all supported
systems, with the exception of macOS 12 and earlier, this rate can be
controlled.
+
The *corrtimeratio* directive sets the ratio between the duration in which the
clock is slewed for an average correction according to the source history and
@@ -676,8 +776,8 @@ that error is corrected. There are four options:
When inserting a leap second, the kernel steps the system clock backwards by
one second when the clock gets to 00:00:00 UTC. When deleting a leap second, it
steps forward by one second when the clock gets to 23:59:59 UTC. This is the
default mode when the system driver supports leap seconds (i.e. on Linux,
FreeBSD, NetBSD and Solaris).
default mode when the system driver supports leap seconds (i.e. all supported
systems with the exception of macOS 12 and earlier).
*step*:::
This is similar to the *system* mode, except the clock is stepped by
*chronyd* instead of the kernel. It can be useful to avoid bugs in the kernel
@@ -732,15 +832,23 @@ clients to safely synchronise with multiple identically configured leap
smearing servers.
[[leapsectz]]*leapsectz* _timezone_::
This directive is used to set the name of the timezone in the system tz
database which *chronyd* can use to find out when will the next leap second
occur. It will periodically check if the times 23:59:59 and 23:59:60 are valid
on Jun 30 and Dec 31 in the timezone. This typically works with the *right/UTC*
timezone.
This directive specifies a timezone in the system tz database which *chronyd*
can use to determine when will the next leap second occur and what is the
current offset between TAI and UTC. It will periodically check if 23:59:59 and
23:59:60 are valid times in the timezone. This typically works with the
_right/UTC_ timezone.
+
This directive is mainly useful with reference clocks which do not provide
leap second information. It is not necessary to restart *chronyd* if the tz
database is updated with a new leap second at least 12 hours before the event.
When a leap second is announced, the timezone needs to be updated at least 12
hours before the leap second. It is not necessary to restart *chronyd*.
+
This directive is useful with reference clocks and other time sources which do
not announce leap seconds, or announce them too late for an NTP server to
forward them to its own clients. Clients of leap smearing servers must not
use this directive.
+
It is also useful when the system clock is required to have correct TAI-UTC
offset. Note that the offset is set only when leap seconds are handled by the
kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*.
+
An example of the directive is:
+
@@ -812,7 +920,7 @@ This directive specifies the maximum assumed drift (frequency error) of the
system clock. It limits the frequency adjustment that *chronyd* is allowed to
use to correct the measured drift. It is an additional limit to the maximum
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
on FreeBSD and NetBSD, 32500 ppm on Solaris).
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
+
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
limited by the system driver rather than this directive.
@@ -847,14 +955,18 @@ The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
to slew the time. It limits the slew rate controlled by the correction time
ratio (which can be set by the <<corrtimeratio,*corrtimeratio*>> directive) and
is effective only on systems where *chronyd* is able to control the rate (i.e.
Linux, FreeBSD, NetBSD, Solaris).
all supported systems with the exception of macOS 12 or earlier).
+
For each system there is a maximum frequency offset of the clock that
can be set by the driver. On Linux it is 100000 ppm, on FreeBSD and NetBSD
it is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel
limitation, setting *maxslewrate* on FreeBSD and NetBSD to a value between 500
For each system there is a maximum frequency offset of the clock that can be set
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
ppm and 5000 ppm will effectively set it to 500 ppm.
+
In early beta releases of macOS 13 this capability is disabled because of a
system kernel bug. When the kernel bug is fixed, chronyd will detect this and
re-enable the capability (see above limitations) with no recompilation required.
+
By default, the maximum slew rate is set to 83333.333 ppm (one twelfth).
[[tempcomp]]
@@ -942,7 +1054,7 @@ both a client of its servers, and a server to other clients.
Examples of the use of the directive are as follows:
+
----
allow foo.example.net
allow 1.2.3.4
allow 1.2
allow 3.4.5
allow 6.7.8/22
@@ -953,7 +1065,8 @@ allow ::/0
allow
----
+
The first directive allows the named node to be an NTP client of this computer.
The first directive allows a node with IPv4 address _1.2.3.4_ to be an NTP
client of this computer.
The second directive allows any node with an IPv4 address of the form _1.2.x.y_
(with _x_ and _y_ arbitrary) to be an NTP client of this computer. Likewise,
the third directive allows any node with an IPv4 address of the form _3.4.5.x_
@@ -994,6 +1107,10 @@ Within a configuration file this capability is probably rather moot; however,
it is of greater use for reconfiguration at run-time via *chronyc* with the
<<chronyc.adoc#allow,*allow all*>> command.
+
The directive allows a hostname to be specified instead of an IP address, but
the name must be resolvable when *chronyd* is started (i.e. *chronyd* needs
to be started when the network is already up and DNS is working).
+
Note, if the <<initstepslew,*initstepslew*>> directive is used in the
configuration file, each of the computers listed in that directive must allow
client access by this computer for it to work.
@@ -1008,25 +1125,20 @@ There is also a *deny all* directive with similar behaviour to the *allow all*
directive.
[[bindaddress]]*bindaddress* _address_::
The *bindaddress* directive allows you to restrict the network interface to
which *chronyd* will listen for NTP requests. This provides an additional level
of access restriction above that available through the <<deny,*deny*>>
mechanism.
The *bindaddress* directive binds the socket on which *chronyd* listens for NTP
requests to a local address of the computer. On systems other than Linux, the
address of the computer needs to be already configured when *chronyd* is
started.
+
Suppose you have a local network with addresses in the _192.168.1.0_
subnet together with an Internet connection. The network interface's IP
address is _192.168.1.1_. Suppose you want to block all access through the
Internet connection. You could add the line:
An example of the use of the directive is:
+
----
bindaddress 192.168.1.1
----
+
to the configuration file.
+
For each of the IPv4 and IPv6 protocols, only one *bindaddress* directive can be
specified. Therefore, it is not useful on computers which should serve NTP on
multiple network interfaces.
Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
directive can be specified. Therefore, it is not useful on computers which
should serve NTP on multiple network interfaces.
[[broadcast]]*broadcast* _interval_ _address_ [_port_]::
The *broadcast* directive is used to declare a broadcast address to which
@@ -1065,8 +1177,10 @@ directive.
[[clientloglimit]]*clientloglimit* _limit_::
This directive specifies the maximum amount of memory that *chronyd* is allowed
to allocate for logging of client accesses. The default limit is 524288 bytes,
which allows monitoring of several thousands of addresses at the same time.
to allocate for logging of client accesses and the state that *chronyd* as an
NTP server needs to support the interleaved mode for its clients. The default
limit is 524288 bytes, which is sufficient for monitoring about four thousand
clients at the same time.
+
In older *chrony* versions if the limit was set to 0, the memory allocation was
unlimited.
@@ -1080,7 +1194,8 @@ clientloglimit 1048576
[[noclientlog]]*noclientlog*::
This directive, which takes no arguments, specifies that client accesses are
not to be logged. Normally they are logged, allowing statistics to be reported
using the <<chronyc.adoc#clients,*clients*>> command in *chronyc*.
using the <<chronyc.adoc#clients,*clients*>> command in *chronyc*. This option
also effectively disables server support for the NTP interleaved mode.
[[local]]*local* [_option_]...::
The *local* directive enables a local reference mode, which allows *chronyd*
@@ -1142,6 +1257,23 @@ An example of the directive is:
local stratum 10 orphan
----
[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
This directive specifies the location of the Samba *ntp_signd* socket when it
is running as a Domain Controller (DC). If *chronyd* is compiled with this
feature, responses to MS-SNTP clients will be signed by the *smbd* daemon.
+
Note that MS-SNTP requests are not authenticated and any client that is allowed
to access the server by the <<allow,*allow*>> directive, or the
<<chronyc.adoc#allow,*allow*>> command in *chronyc*, can get an MS-SNTP
response signed with a trust account's password and try to crack the password
in a brute-force attack. Access to the server should be carefully controlled.
+
An example of the directive is:
+
----
ntpsigndsocket /var/lib/samba/ntp_signd
----
[[port]]*port* _port_::
This option allows you to configure the port on which *chronyd* will listen for
NTP requests. The port will be open only when an address is allowed by the
@@ -1171,7 +1303,10 @@ in any order):
*interval*:::
This option sets the minimum interval between responses. It is defined as a
power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
is -4 and the maximum value is 12.
is -19 (524288 packets per second) and the maximum value is 12 (one packet per
4096 seconds). Note that with values below -4 the rate limiting is coarse
(responses are allowed in bursts, even if the interval between them is shorter
than the specified interval).
*burst*:::
This option sets the maximum number of responses that can be sent in a burst,
temporarily exceeding the limit specified by the *interval* option. This is
@@ -1183,20 +1318,20 @@ This option sets the rate at which responses are randomly allowed even if the
limits specified by the *interval* and *burst* options are exceeded. This is
necessary to prevent an attacker who is sending requests with a spoofed
source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 3 by default, i.e. on average at
least every eighth request has a response. The minimum value is 1 and the
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the
maximum value is 4.
::
+
An example use of the directive is:
+
----
ratelimit interval 4 burst 4
ratelimit interval 1 burst 16
----
+
This would reduce the response rate for IP addresses that send packets on
average more frequently than once per 16 seconds or send packets in bursts
of more than 4 packets.
This would reduce the response rate for IP addresses sending packets on average
more than once per 2 seconds, or sending packets in bursts of more than 16
packets, by up to 75% (with default *leak* of 2).
[[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
The *smoothtime* directive can be used to enable smoothing of the time that
@@ -1251,16 +1386,17 @@ smoothtime 50000 0.01
=== Command and monitoring access
[[bindcmdaddress]]*bindcmdaddress* _address_::
The *bindcmdaddress* directive allows you to specify the network interface on
which *chronyd* will listen for monitoring command packets (issued by
*chronyc*). This provides an additional level of access restriction above that
available through the <<cmddeny,*cmddeny*>> mechanism.
The *bindcmdaddress* directive allows you to specify an IP address of an
interface on which *chronyd* will listen for monitoring command packets (issued
by *chronyc*). On systems other than Linux, the address of the interface needs
to be already configured when *chronyd* is started.
+
This directive can also change the path of the Unix domain command socket,
which is used by *chronyc* to send configuration commands. The socket must be
in a directory that is accessible only by the root or _chrony_ user. The
directory will be created on start if it does not exist. The compiled-in default
path of the socket is _@CHRONYSOCKDIR@/chronyd.sock_.
path of the socket is _@CHRONYRUNDIR@/chronyd.sock_. The socket can be
disabled by setting the path to _/_.
+
By default, *chronyd* binds to the loopback interface (with addresses
_127.0.0.1_ and _::1_). This blocks all access except from localhost. To listen
@@ -1273,8 +1409,8 @@ bindcmdaddress ::
+
to the configuration file.
+
For each of the IPv4 and IPv6 protocols, only one *bindcmdaddress* directive can be
specified.
For each of the IPv4, IPv6, and Unix domain protocols, only one
*bindcmdaddress* directive can be specified.
+
An example that sets the path of the Unix domain command socket is:
+
@@ -1324,8 +1460,8 @@ need to be run with the *-p 257* switch to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is
similar to the <<ratelimit,*ratelimit*>> directive, except responses to
localhost are never limited and the default interval is 1 (2 seconds), the default
burst is 16, and the default leak rate is 2.
localhost are never limited and the default interval is -4 (16 packets per
second).
+
An example of the use of the directive is:
+
@@ -1368,7 +1504,7 @@ This would set the threshold error to 30 seconds.
[[rtcdevice]]*rtcdevice* _device_::
The *rtcdevice* directive sets the path to the device file for accessing the
RTC. The default path is _/dev/rtc_.
RTC. The default path is _@DEFAULT_RTC_DEVICE@_.
[[rtcfile]]*rtcfile* _file_::
The *rtcfile* directive defines the name of the file in which *chronyd* can
@@ -1421,7 +1557,7 @@ cannot be used with the <<rtcfile,*rtcfile*>> directive.
+
On Linux, the RTC copy is performed by the kernel every 11 minutes.
+
On Mac OS X, <<chronyd,*chronyd*>> will perform the RTC copy every 60 minutes
On macOS, <<chronyd,*chronyd*>> will perform the RTC copy every 60 minutes
when the system clock is in a synchronised state.
+
On other systems this directive does nothing.
@@ -1434,14 +1570,16 @@ The log files are written to the directory specified by the <<logdir,*logdir*>>
directive. A banner is periodically written to the files to indicate the
meanings of the columns.
+
*measurements*:::
*rawmeasurements*:::
This option logs the raw NTP measurements and related information to a file
called _measurements.log_. An example line (which actually appears as a single
line in the file) from the log file is shown below.
called _measurements.log_. An entry is made for each packet received from the
source. This can be useful when debugging a problem. An example line (which
actually appears as a single line in the file) from the log file is shown
below.
+
----
2015-10-13 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
-4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03
2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
-4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 CB00717B 4B D K
----
+
The columns are as follows (the quantities in square brackets are the values
@@ -1471,6 +1609,19 @@ from the example line above):
. The peer dispersion (_epsilon_ in RFC 5905). [1.577e-05]
. The root delay (_DELTA_ in RFC 5905). [1.615e-01]
. The root dispersion (_EPSILON_ in RFC 5905). [7.446e-03]
. Reference ID of the server's source as a hexadecimal number. [CB00717B]
. NTP mode of the received packet (_1_=active peer, _2_=passive peer,
_3_=server, _B_=basic, _I_=interleaved). [4B]
. Source of the local transmit timestamp
(_D_=daemon, _K_=kernel, _H_=hardware). [D]
. Source of the local receive timestamp
(_D_=daemon, _K_=kernel, _H_=hardware). [K]
+
*measurements*:::
This option is identical to the *rawmeasurements* option, except it logs only
valid measurements from synchronised sources, i.e. measurements which passed
the RFC 5905 tests 1 through 7. This can be useful for producing graphs of the
source's performance.
+
*statistics*:::
This option logs information about the regression processing to a file called
@@ -1478,8 +1629,8 @@ _statistics.log_. An example line (which actually appears as a single line in
the file) from the log file is shown below.
+
----
2015-07-22 05:40:50 203.0.113.15 6.261e-03 -3.247e-03 \
2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8
2016-08-10 05:40:50 203.0.113.15 6.261e-03 -3.247e-03 \
2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8 0.00
----
+
The columns are as follows (the quantities in square brackets are the values
@@ -1514,6 +1665,11 @@ from the example line above):
to be discarded. The number of runs for the data that is being retained is
tabulated. Values of approximately half the number of samples are expected.
[8]
. The estimated asymmetry of network jitter on the path to the source which was
used to correct the measured offsets. The asymmetry can be between -0.5 and
0.5. A negative value means the delay of packets sent to the source is
more variable than the delay of packets sent from the source back. [0.00,
i.e. no correction for asymmetry]
+
*tracking*:::
This option logs changes to the estimate of the system's gain or loss rate, and
@@ -1693,6 +1849,83 @@ sendmail binary.
=== Miscellaneous
[[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
This directive enables hardware timestamping of NTP packets sent to and
received from the specified network interface. The network interface controller
(NIC) uses its own clock to accurately timestamp the actual transmissions and
receptions, avoiding processing and queueing delays in the kernel, network
driver, and hardware. This can significantly improve the accuracy of the
timestamps and the measured offset, which is used for synchronisation of the
system clock. In order to get the best results, both sides receiving and
sending NTP packets (i.e. server and client, or two peers) need to use HW
timestamping. If the server or peer supports the interleaved mode, it needs to
be enabled by the *xleave* option in the <<server,*server*>> or the
<<peer,*peer*>> directive.
+
This directive is supported on Linux 3.19 and newer. The NIC must support HW
timestamping, which can be verified with the *ethtool -T* command. The list of
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
_SOF_TIMESTAMPING_TX_HARDWARE_, _SOF_TIMESTAMPING_RX_HARDWARE_, and the filter
modes should include _HWTSTAMP_FILTER_ALL_ or _HWTSTAMP_FILTER_NTP_ALL_. When
*chronyd* is running, no other process (e.g. a PTP daemon) should be working
with the NIC clock.
+
If the kernel supports software timestamping, it will be enabled for all
interfaces. With both hardware and software timestamping there are some
limitations on which timestamps can be actually used, e.g. transmit
timestamping does not currently work with IPv6 packets using IP options and
hardware timestamping of packets received from bridges, bonds, and other
virtual interfaces, works only on Linux 4.13 and newer. The source of
timestamps (i.e. hardware, kernel, or daemon) is indicated in the
_measurements.log_ file if enabled by the <<log,*log measurements*>> directive,
and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in *chronyc*.
+
If the specified interface is _*_, *chronyd* will try to enable HW timestamping
on all available interfaces.
+
The *hwtimestamp* directive has the following options:
+
*minpoll* _poll_:::
This option specifies the minimum interval between readings of the NIC clock.
It's defined as a power of two. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
of a second).
*precision* _precision_:::
This option specifies the assumed precision of reading of the NIC clock. The
default value is 100e-9 (100 nanoseconds).
*txcomp* _compensation_:::
This option specifies the difference in seconds between the actual transmission
time at the physical layer and the reported transmit timestamp. This value will
be added to transmit timestamps obtained from the NIC. The default value is 0.
*rxcomp* _compensation_:::
This option specifies the difference in seconds between the reported receive
timestamp and the actual reception time at the physical layer. This value will
be subtracted from receive timestamps obtained from the NIC. The default value
is 0.
*nocrossts*:::
Some hardware can precisely cross timestamp the NIC clock with the system
clock. This option disables the use of the cross timestamping.
*rxfilter* _filter_:::
This option selects the receive timestamping filter. Possible values are:
_all_, _ntp_, and _none_. The default value is _ntp_, which enables
timestamping of NTP packets (_HWTSTAMP_FILTER_NTP_ALL_) if it is supported, or
timestamping of all packets (_HWTSTAMP_FILTER_ALL_). Setting *rxfilter* to
_all_ forces timestamping of all packets, which can be useful when the NIC
supports both filters and NTP packets are received from or on a non-standard
UDP port (e.g. specified by the *port* directive). Setting *rxfilter* to _none_
disables receive HW timestamping and allows transmit HW timestamping to be
enabled when the NIC supports only PTP-specific receive filters.
::
+
Examples of the directive are:
+
----
hwtimestamp eth0
hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
----
[[include]]*include* _pattern_::
The *include* directive includes a configuration file or multiple configuration
files if a wildcard pattern is specified. This can be useful when maintaining
@@ -1755,8 +1988,8 @@ significant impact on performance as *chronyd's* memory usage is modest. The
[[pidfile]]*pidfile* _file_::
*chronyd* always writes its process ID (PID) to a file, and checks this file on
startup to see if another *chronyd* may already be running on the system. By
default, the file used is _/var/run/chronyd.pid_. The *pidfile* directive
startup to see if another *chronyd* might already be running on the system. By
default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
allows the name to be changed, e.g.:
+
----
@@ -1765,8 +1998,8 @@ pidfile /run/chronyd.pid
[[sched_priority]]*sched_priority* _priority_::
On Linux, the *sched_priority* directive will select the SCHED_FIFO real-time
scheduler at the specified priority (which must be between 0 and 100). On Mac
OS X, this option must have either a value of 0 (the default) to disable the
scheduler at the specified priority (which must be between 0 and 100). On
macOS, this option must have either a value of 0 (the default) to disable the
thread time constraint policy or 1 for the policy to be enabled. Other systems
do not support this option.
+
@@ -1781,7 +2014,7 @@ wait for the scheduler to get around to running it. You should not use this
unless you really need it. The *sched_setscheduler(2)* man page has more
details.
+
On Mac OS X, this directive uses the *thread_policy_set()* kernel call to
On macOS, this directive uses the *thread_policy_set()* kernel call to
specify real-time scheduling. As noted for Linux, you should not use this
directive unless you really need it.
@@ -1790,7 +2023,7 @@ The *user* directive sets the name of the system user to which *chronyd* will
switch after start in order to drop root privileges.
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On Mac OS X, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
+
@@ -2010,8 +2243,7 @@ is made of the RTC error at a particular RTC second, and the rate at which the
RTC gains or loses time relative to true time.
When the computer is powered down, the measurement histories for all the NTP
servers are saved to files (if the <<dumponexit,*dumponexit*>> directive is
specified in the configuration file), and the RTC tracking information is also
servers are saved to files, and the RTC tracking information is also
saved to a file (if the <<rtcfile,*rtcfile*>> directive has been specified).
These pieces of information are also saved if the <<chronyc.adoc#dump,*dump*>>
and <<chronyc.adoc#writertc,*writertc*>> commands respectively are issued
@@ -2064,7 +2296,6 @@ log statistics measurements tracking
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
maxupdateskew 100.0
dumponexit
dumpdir @CHRONYVARDIR@
rtcfile @CHRONYVARDIR@/rtc
----
@@ -2094,6 +2325,43 @@ For the system shutdown, *chronyd* should receive a SIGTERM several seconds
before the final SIGKILL; the SIGTERM causes the measurement histories and RTC
information to be saved.
=== Public NTP server
*chronyd* can be configured to operate as a public NTP server, e.g. to join the
http://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration
is similar to the NTP client with permanent connection, except it needs to
allow client access from all addresses. It is recommended to find at least four
good servers (e.g. from the pool, or on the NTP homepage). If the server has a
hardware reference clock (e.g. a GPS receiver), it can be specified by the
<<refclock,*refclock*>> directive.
The amount of memory used for logging client accesses can be increased in order
to enable clients to use the interleaved mode even when the server has a large
number of clients, and better support rate limiting if it is enabled by the
<<ratelimit,*ratelimit*>> directive. The system timezone database, if it is
kept up to date and includes the _right/UTC_ timezone, can be used as a
reliable source to determine when a leap second will be applied to UTC. The
*-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which
*chronyd* will not be able to serve time to its clients when it needs to be
restarted (e.g. after upgrading to a newer version, or a change in the
configuration).
The configuration file could look like:
----
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
server qux.example.net iburst
makestep 1.0 3
rtcsync
allow
clientloglimit 100000000
leapsectz right/UTC
driftfile @CHRONYVARDIR@/drift
dumpdir @CHRONYRUNDIR@
----
== SEE ALSO
<<chronyc.adoc#,*chronyc(1)*>>, <<chronyd.adoc#,*chronyd(8)*>>

View File

@@ -1,6 +1,7 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2016
//
// This program is free software; you can redistribute it and/or modify
@@ -44,7 +45,7 @@ There are two ways *chronyc* can access *chronyd*. One is the Internet
Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
accessible locally by the root or _chrony_ user. By default, *chronyc* first
tries to connect to the Unix domain socket. The compiled-in default path is
_@CHRONYSOCKDIR@/chronyd.sock_. If that fails (e.g. because *chronyc* is
_@CHRONYRUNDIR@/chronyd.sock_. If that fails (e.g. because *chronyc* is
running under a non-root user), it will try to connect to 127.0.0.1 and then
::1.
@@ -74,8 +75,8 @@ With this option hostnames will be resolved only to IPv4 addresses.
With this option hostnames will be resolved only to IPv6 addresses.
*-n*::
This option disables resolving of IP addresses to hostnames (e.g. to avoid slow
DNS lookups).
This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
DNS lookups. Long addresses will not be truncated to fit into the column.
*-c*::
This option enables printing of reports in a comma-separated values (CSV)
@@ -127,17 +128,17 @@ The *tracking* command displays parameters about the system's clock
performance. An example of the output is shown below.
+
----
Reference ID : 203.0.113.15 (foo.example.net)
Reference ID : CB00710F (foo.example.net)
Stratum : 3
Ref time (UTC) : Fri Feb 3 15:00:29 2012
System time : 0.000001501 seconds slow of NTP time
Last offset : -0.000001632 seconds
RMS offset : 0.000002360 seconds
Frequency : 331.898 ppm fast
Residual freq : 0.004 ppm
Skew : 0.154 ppm
Root delay : 0.373169 seconds
Root dispersion : 0.024780 seconds
Ref time (UTC) : Fri Jan 27 09:49:17 2017
System time : 0.000006523 seconds slow of NTP time
Last offset : -0.000006747 seconds
RMS offset : 0.000035822 seconds
Frequency : 3.225 ppm slow
Residual freq : -0.000 ppm
Skew : 0.129 ppm
Root delay : 0.013639022 seconds
Root dispersion : 0.001100737 seconds
Update interval : 64.2 seconds
Leap status : Normal
----
@@ -150,10 +151,14 @@ computer is currently synchronised. For IPv4 addresses, the reference ID is
equal to the address and for IPv6 addresses it is the first 32 bits of the MD5
sum of the address.
+
If it is _127.127.1.1_ it means the computer is not synchronised to any
external source and that you have the _local_ mode operating (via the
<<local,*local*>> command in *chronyc*, or the
If the reference ID is _7F7F0101_ and there is no name or IP address, it means
the computer is not synchronised to any external source and that you have the
_local_ mode operating (via the <<local,*local*>> command in *chronyc*, or the
<<chrony.conf.adoc#local,*local*>> directive in the configuration file).
+
The reference ID is printed as a hexadecimal number. Note that in older
versions it used to be printed in quad-dotted notation and could be confused
with an IPv4 address.
*Stratum*:::
The stratum indicates how many hops away from a computer with an attached
reference clock we are. Such a computer is a stratum-1 computer, so the
@@ -182,9 +187,6 @@ The '`frequency`' is the rate by which the system's clock would be wrong if
For example, a value of 1 ppm would mean that when the system's clock thinks it
has advanced 1 second, it has actually advanced by 1.000001 seconds relative to
true time.
+
As you can see in the example, the clock in the computer is not a very
good one; it would gain about 30 seconds per day if it was not corrected!
*Residual freq*:::
This shows the '`residual frequency`' for the currently selected reference
source. This reflects any difference between what the measurements from the
@@ -437,6 +439,92 @@ the offline state.
the name of the server or peer was not resolved to an address yet; this source is
not visible in the *sources* and *sourcestats* reports.
[[ntpdata]]*ntpdata* [_address_]::
The *ntpdata* command displays the last valid measurement and other
NTP-specific information about the specified NTP source, or all NTP sources if
no address was specified. An example of the output is shown below.
+
----
Remote address : 203.0.113.15 (CB00710F)
Remote port : 123
Local address : 203.0.113.74 (CB00714A)
Leap status : Normal
Version : 4
Mode : Server
Stratum : 1
Poll interval : 10 (1024 seconds)
Precision : -24 (0.000000060 seconds)
Root delay : 0.000015 seconds
Root dispersion : 0.000015 seconds
Reference ID : 47505300 (GPS)
Reference time : Fri Nov 25 15:22:12 2016
Offset : -0.000060878 seconds
Peer delay : 0.000175634 seconds
Peer dispersion : 0.000000681 seconds
Response time : 0.000053050 seconds
Jitter asymmetry: +0.00
NTP tests : 111 111 1111
Interleaved : No
Authenticated : No
TX timestamping : Kernel
RX timestamping : Kernel
Total TX : 24
Total RX : 24
Total valid RX : 24
----
+
The fields are explained as follows:
+
*Remote address*:::
The IP address of the NTP server or peer, and the corresponding reference ID.
*Remote port*:::
The UDP port number to which the request was sent. The standard NTP port is
123.
*Local address*:::
The local IP address which received the response, and the corresponding
reference ID.
*Leap status*:::
*Version*:::
*Mode*:::
*Stratum*:::
*Poll interval*:::
*Precision*:::
*Root delay*:::
*Root dispersion*:::
*Reference ID*:::
*Reference time*:::
The NTP values from the last valid response.
*Offset*:::
*Peer delay*:::
*Peer dispersion*:::
The measured values.
*Response time*:::
The time the server or peer spent in processing of the request and waiting
before sending the response.
*Jitter asymmetry*:::
The estimated asymmetry of network jitter on the path to the source. The
asymmetry can be between -0.5 and 0.5. A negative value means the delay of
packets sent to the source is more variable than the delay of packets sent
from the source back.
*NTP tests*:::
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
delay, delay ratio, delay dev ratio, and synchronisation loop.
*Interleaved*:::
This shows if the response was in the interleaved mode.
*Authenticated*:::
This shows if the response was authenticated.
*TX timestamping*:::
The source of the local transmit timestamp. Valid values are _Daemon_,
_Kernel_, and _Hardware_.
*RX timestamping*:::
The source of the local receive timestamp.
*Total TX*:::
The number of packets sent to the source.
*Total RX*:::
The number of all packets received from the source.
*Total valid RX*:::
The number of valid packets received from the source.
[[add_peer]]*add peer* _address_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
*chronyd* is running.
@@ -1030,17 +1118,15 @@ purged. An example of how to do this is shown below.
[[dump]]*dump*::
The *dump* command causes *chronyd* to write its current history of
measurements for each of its sources to dump files, either for inspection or to
support the *-r* option when *chronyd* is restarted.
+
The *dump* command is somewhat equivalent to the
<<chrony.conf.adoc#dumponexit,*dumponexit*>> directive in the configuration
file.
+
To use the *dump* command, you might want to configure the name of the
directory into which the dump files will be written. This can only be
done in the configuration file with the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive.
measurements for each of its sources to dump files in the directory specified
in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive. Note that *chronyd* does this automatically when it exits. This
command is mainly useful for inspection of the history whilst *chronyd* is
running.
[[rekey]]*rekey*::
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
=== Client commands

View File

@@ -62,27 +62,33 @@ When run in this mode, the program will not detach itself from the terminal.
*-d*::
When run in this mode, the program will not detach itself from the terminal,
and all messages will be sent to the terminal instead of to syslog. When
and all messages will be written to the terminal instead of syslog. When
*chronyd* was compiled with debugging support, this option can be used twice to
print also debugging messages.
*-l* _file_::
This option specifies a file which should be used for logging instead of syslog
or terminal.
*-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal.
*-Q*::
This option is similar to *-q*, but it will only print the offset without any
corrections of the clock.
This option is similar to the *-q* option, except it only prints the offset
without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
*-r*::
This option will reload sample histories for each of the servers and refclocks
being used. These histories are created by using the
<<chronyc.adoc#dump,*dump*>> command in *chronyc*, or by setting the
<<chrony.conf.adoc#dumponexit,*dumponexit*>> directive in the configuration
file. This option is useful if you want to stop and restart *chronyd* briefly
for any reason, e.g. to install a new version. However, it should be used only
on systems where the kernel can maintain clock compensation whilst not under
*chronyd*'s control (i.e. Linux, FreeBSD, NetBSD and Solaris).
This option will try to reload and then delete files containing sample
histories for each of the servers and reference clocks being used. These
histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
*chronyc*, or by setting the <<chrony.conf.adoc#dumponexit,*dumponexit*>>
directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
Solaris, and macOS 10.13 or later).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -109,13 +115,20 @@ time and the RTC time, the system time will be set to it to restore the time
when *chronyd* was previously stopped. This is useful on computers that have no
RTC or the RTC is broken (e.g. it has no battery).
*-t* _timeout_::
This option sets a timeout (in seconds) after which *chronyd* will exit. If the
clock is not synchronised, it will exit with a non-zero status. This is useful
with the *-q* or *-Q* option to shorten the maximum time waiting for
measurements, or with the *-r* option to limit the time when *chronyd* is
running, but still allow it to adjust the frequency of the system clock.
*-u* _user_::
This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive (default _@DEFAULT_USER@_).
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On Mac OS X, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
@@ -134,7 +147,7 @@ killed even in normal operation.
*-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
specified priority (which must be between 0 and 100). On Mac OS X, this option
specified priority (which must be between 0 and 100). On macOS, this option
must have either a value of 0 (the default) to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
support this option.
@@ -143,6 +156,13 @@ support this option.
This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux.
*-x*::
This option disables the control of the system clock. *chronyd* will not make
any adjustments of the clock, but it will still track its offset and frequency
relative to the estimated true time, and be able to operate as an NTP server.
This allows *chronyd* to run without the capability to adjust or set the system
clock (e.g. in some containers).
*-v*::
With this option *chronyd* will print version number to the terminal and exit.
@@ -161,4 +181,4 @@ https://chrony.tuxfamily.org/.
== AUTHORS
chrony was written by Richard Curnow, Miroslav Lichvar and others.
chrony was written by Richard Curnow, Miroslav Lichvar, and others.

View File

@@ -61,8 +61,8 @@ that.
In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialized on the
next boot from the RTC, the `rtcsync` directive enables a mode in which the
system time is periodically copied to the RTC. It is supported on Linux and Mac
OS X.
system time is periodically copied to the RTC. It is supported on Linux and
macOS.
If you want to use public NTP servers from the
http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
@@ -79,7 +79,7 @@ rtcsync
You need to add an `allow` directive to the _chrony.conf_ file in order to open
the NTP port and allow `chronyd` to reply to client requests. `allow` with no
specified subnet allows all IPv4 and IPv6 addresses.
specified subnet allows access from all IPv4 and IPv6 addresses.
=== I have several computers on a LAN. Should be all clients of an external server?
@@ -97,7 +97,7 @@ _chrony.conf_ file. This configuration will be better because
No. Starting from version 1.25, `chronyd` will keep trying to resolve
the names specified by the `server`, `pool`, and `peer` directives in an
increasing interval until it succeeds. The `online` command can be issued from
`chronyc` to try to resolve them immediately.
`chronyc` to force `chronyd` to try to resolve the names immediately.
=== How can I make `chronyd` more secure?
@@ -148,15 +148,20 @@ network. It's better to use more than one server, three or four is usually
recommended as the minimum, so `chronyd` can detect servers that serve false
time and combine measurements from multiple sources.
If you have a network card with hardware timestamping supported on Linux, it
can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It
should make local receive and transmit timestamps of NTP packets much more
accurate.
There are also useful options which can be set in the `server` directive, they
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio` and
`maxdelaydevratio`.
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`,
`maxdelaydevratio`, and `xleave`.
The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their
default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 6 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers or if have
`maxpoll` and 8 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers, or if you have
permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system
clock.
@@ -189,6 +194,28 @@ with local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
If your server supports the interleaved mode, the `xleave` option should be
added to the `server` directive in order to allow the server to send the
client more accurate hardware or kernel transmit timestamps. When combined with
local hardware timestamping, sub-microsecond accuracy may be possible. An
example could be
----
server ntp.local minpoll 2 maxpoll 2 xleave
hwtimestamp eth0
----
=== Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
With the `-Q` option it will print the measured offset without setting the
clock. If you don't want to use a configuration file, NTP servers can be
specified on the command line. For example:
----
# chronyd -q 'pool pool.ntp.org iburst'
----
=== What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the
@@ -227,8 +254,17 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
=== Are NTP servers specified with the `offline` option?
Check that you're using ``chronyc``'s `online` and `offline` commands
appropriately. Again, check in _measurements.log_ to see if you're getting any
data back from the server.
appropriately. The `activity` command prints the number of sources that are
currently online and offline. For example:
----
200 OK
3 sources online
0 sources offline
0 sources doing burst (return to online)
0 sources doing burst (return to offline)
0 sources with unknown address
----
=== Is `chronyd` allowed to step the system clock?
@@ -287,12 +323,15 @@ authentication (`commandkey` directive).
=== Why does `chronyc tracking` always print an IPv4 address as reference ID?
The reference ID is a 32-bit value and is always printed in quad-dotted
notation, even if the reference source doesn't have an IPv4 address. For IPv4
addresses, the reference ID is equal to the address, but for IPv6 addresses it
is the first 32 bits of the MD5 sum of the address. For reference clocks, the
reference ID is the value specified with the `refid` option in the `refclock`
directive.
The reference ID is a 32-bit value and in versions before 3.0 it was printed in
quad-dotted notation, even if the reference source did not actually have an
IPv4 address. For IPv4 addresses, the reference ID is equal to the address, but
for IPv6 addresses it is the first 32 bits of the MD5 sum of the address. For
reference clocks, the reference ID is the value specified with the `refid`
option in the `refclock` directive.
Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
confusion with IPv4 addresses.
If you need to get the IP address of the current reference source, use the `-n`
option to disable resolving of IP addresses and read the second field (printed
@@ -373,19 +412,20 @@ to serve time to clients in the network which support the broadcast client mode
=== Can `chronyd` keep the system clock a fixed offset away from real time?
This is not possible as the program currently stands.
Yes. Starting from version 3.0, an offset can be specified by the `offset`
option for all time sources in the _chrony.conf_ file.
=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
`chronyd` will keep trying to access the server(s) that it thinks are online.
When the network is connected again, it will take some time (on average half of
the maximum polling interval) before new measurements are made and the clock is
corrected. If the servers were set to offline and the `online` command was
issued when the network was connected, `chronyd` would make new measurements
immediately.
`chronyd` will keep trying to access the sources that it thinks are online, and
it will take longer before new measurements are actually made and the clock is
corrected when the network is connected again. If the sources were set to
offline, `chronyd` would make new measurements immediately after issuing the
`online` command.
The `auto_offline` option to the `server` entry in the _chrony.conf_ file may
be useful to switch the servers to the offline state automatically.
Unless the network connection lasts only few minutes (less than the maximum
polling interval), the delay is usually not a problem, and it may be acceptable
to keep all sources online all the time.
== Operating systems

View File

@@ -1,5 +1,6 @@
[Unit]
Description=Wait for chrony to synchronize system clock
Documentation=man:chronyc(1)
After=chronyd.service
Requires=chronyd.service
Before=time-sync.target
@@ -9,7 +10,7 @@ Wants=time-sync.target
Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc waitsync 600 0.1 0.0 1
ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1
RemainAfterExit=yes
StandardOutput=null

View File

@@ -4,8 +4,8 @@ pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 1 second.
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).

View File

@@ -5,17 +5,24 @@ pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 1 second.
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync
# Allow NTP client access from local network.
#allow 192.168/16
# Enable hardware timestamping on all interfaces that support it.
#hwtimestamp *
# Serve time even if not synchronized to any NTP server.
# Increase the minimum number of selectable sources required to adjust
# the system clock.
#minsources 2
# Allow NTP client access from local network.
#allow 192.168.0.0/16
# Serve time even if not synchronized to a time source.
#local stratum 10
# Specify file containing keys for NTP authentication.

View File

@@ -33,42 +33,30 @@
! pool pool.ntp.org iburst
# However, for dial-up use you probably want these instead. The word
# 'offline' means that the server is not visible at boot time. Use
# chronyc's 'online' command to tell chronyd that these servers have
# become visible after you go on-line.
! server foo.example.net offline
! server bar.example.net offline
! server baz.example.net offline
! pool pool.ntp.org offline
# You may want to specify NTP 'peers' instead. If you run a network
# with a lot of computers and want several computers running chrony to
# have the 'front-line' interface to the public NTP servers, you can
# 'peer' these machines together to increase robustness.
! peer foo.example.net
# There are other options to the 'server' and 'peer' directives that you
# might want to use. For example, you can ignore measurements whose
# round-trip-time is too large (indicating that the measurement is
# probably useless, because you don't know which way the measurement
# message got held up.) Consult the full documentation for details.
#######################################################################
### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK
#
# To avoid changes being made to your computer's gain/loss compensation
# when the measurement history is too erratic, you might want to enable
# one of the following lines. The first seems good for dial-up (or
# other high-latency connections like slow leased lines), the second
# seems OK for a LAN environment.
# one of the following lines. The first seems good with servers on the
# Internet, the second seems OK for a LAN environment.
! maxupdateskew 100
! maxupdateskew 5
# If you want to increase the minimum number of selectable sources
# required to update the system clock in order to make the
# synchronisation more reliable, uncomment (and edit) the following
# line.
! minsources 2
# If your computer has a good stable clock (e.g. it is not a virtual
# machine), you might also want to reduce the maximum assumed drift
# (frequency error) of the clock (the value is specified in ppm).
! maxdrift 100
#######################################################################
### FILENAMES ETC
# Chrony likes to keep information about your computer's clock in files.
@@ -181,13 +169,12 @@ driftfile /var/lib/chrony/drift
# machine accesses it. The information can be accessed by the 'clients'
# command of chronyc. You can disable this facility by uncommenting the
# following line. This will save a bit of memory if you have many
# clients.
# clients and it will also disable support for the interleaved mode.
! noclientlog
# The clientlog size is limited to 512KB by default. If you have many
# clients, especially in many different subnets, you might want to
# increase the limit.
# clients, you might want to increase the limit.
! clientloglimit 4194304
@@ -196,7 +183,7 @@ driftfile /var/lib/chrony/drift
# clients that are sending requests too frequently, uncomment and edit
# the following line.
! limitrate interval 3 burst 8
! ratelimit interval 3 burst 8
#######################################################################
### REPORTING BIG CLOCK CHANGES
@@ -243,7 +230,17 @@ driftfile /var/lib/chrony/drift
# Rate limiting can be enabled also for command packets. (Note,
# commands from localhost are never limited.)
! cmdratelimit interval 1 burst 16
! cmdratelimit interval -4 burst 16
#######################################################################
### HARDWARE TIMESTAMPING
# On Linux, if the network interface controller and its driver support
# hardware timestamping, it can significantly improve the accuracy of
# synchronisation. It can be enabled on specified interfaces only, or it
# can be enabled on all interfaces that support it.
! hwtimestamp eth0
! hwtimestamp *
#######################################################################
### REAL TIME CLOCK
@@ -274,6 +271,12 @@ driftfile /var/lib/chrony/drift
! rtcdevice /dev/misc/rtc
# Alternatively, if not using the -s option, this directive can be used
# to enable a mode in which the RTC is periodically set to the system
# time, with no tracking of its drift.
! rtcsync
#######################################################################
### REAL TIME SCHEDULER
# This directive tells chronyd to use the real-time FIFO scheduler with the

View File

@@ -1,13 +1,18 @@
[Unit]
Description=NTP client/server
Documentation=man:chronyd(8) man:chrony.conf(5)
After=ntpdate.service sntp.service ntpd.service
Conflicts=ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
PIDFile=/var/run/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
[Install]
WantedBy=multi-user.target

209
hwclock.c Normal file
View File

@@ -0,0 +1,209 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Tracking of hardware clocks (e.g. RTC, PHC)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "regress.h"
#include "util.h"
/* Maximum number of samples per clock */
#define MAX_SAMPLES 16
struct HCL_Instance_Record {
/* HW and local reference timestamp */
struct timespec hw_ref;
struct timespec local_ref;
/* Samples stored as intervals (uncorrected for frequency error)
relative to local_ref and hw_ref */
double x_data[MAX_SAMPLES];
double y_data[MAX_SAMPLES];
/* Number of samples */
int n_samples;
/* Maximum error of the last sample */
double last_err;
/* Minimum interval between samples */
double min_separation;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */
double offset;
double frequency;
};
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
HCL_Instance clock;
double delta;
clock = anything;
if (clock->n_samples)
UTI_AdjustTimespec(&clock->local_ref, cooked, &clock->local_ref, &delta, dfreq, doffset);
if (clock->valid_coefs)
clock->frequency /= 1.0 - dfreq;
}
/* ================================================== */
HCL_Instance
HCL_CreateInstance(double min_separation)
{
HCL_Instance clock;
clock = MallocNew(struct HCL_Instance_Record);
clock->x_data[MAX_SAMPLES - 1] = 0.0;
clock->y_data[MAX_SAMPLES - 1] = 0.0;
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
LCL_AddParameterChangeHandler(handle_slew, clock);
return clock;
}
/* ================================================== */
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
Free(clock);
}
/* ================================================== */
int
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
{
if (!clock->n_samples ||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= clock->min_separation)
return 1;
return 0;
}
/* ================================================== */
void
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err)
{
double hw_delta, local_delta, local_freq, raw_freq;
int i, n_runs, best_start;
local_freq = 1.0 - LCL_ReadAbsoluteFrequency() / 1.0e6;
/* Shift old samples */
if (clock->n_samples) {
if (clock->n_samples >= MAX_SAMPLES)
clock->n_samples--;
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
if (hw_delta <= 0.0 || local_delta < clock->min_separation / 2.0) {
clock->n_samples = 0;
DEBUG_LOG("HW clock reset interval=%f", local_delta);
}
for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
clock->x_data[i - 1] = clock->x_data[i] - local_delta;
}
}
clock->n_samples++;
clock->hw_ref = *hw_ts;
clock->local_ref = *local_ts;
clock->last_err = err;
/* Get new coefficients */
clock->valid_coefs =
RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
clock->y_data + MAX_SAMPLES - clock->n_samples,
clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
&n_runs, &best_start);
if (!clock->valid_coefs) {
DEBUG_LOG("HW clock needs more samples");
return;
}
clock->frequency = raw_freq / local_freq;
/* Drop unneeded samples */
clock->n_samples -= best_start;
/* If the fit doesn't cross the error interval of the last sample, throw away
all previous samples and keep only the frequency estimate */
if (fabs(clock->offset) > err) {
DEBUG_LOG("HW clock reset offset=%e", clock->offset);
clock->offset = 0.0;
clock->n_samples = 1;
}
DEBUG_LOG("HW clock samples=%d offset=%e freq=%.9e raw_freq=%.9e err=%e ref_diff=%e",
clock->n_samples, clock->offset, clock->frequency, raw_freq, err,
UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
}
/* ================================================== */
int
HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked, double *err)
{
double offset, elapsed;
if (!clock->valid_coefs)
return 0;
elapsed = UTI_DiffTimespecsToDouble(raw, &clock->hw_ref);
offset = clock->offset + elapsed / clock->frequency;
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
/* Fow now, just return the error of the last sample */
if (err)
*err = clock->last_err;
return 1;
}

48
hwclock.h Normal file
View File

@@ -0,0 +1,48 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header for tracking of hardware clocks */
#ifndef GOT_HWCLOCK_H
#define GOT_HWCLOCK_H
typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(double min_separation);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err);
/* Convert raw hardware time to cooked local time */
extern int HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
double *err);
#endif

113
keys.c
View File

@@ -103,9 +103,9 @@ static int
determine_hash_delay(uint32_t key_id)
{
NTP_Packet pkt;
struct timeval before, after;
unsigned long usecs, min_usecs=0;
int i;
struct timespec before, after;
double diff, min_diff;
int i, nsecs;
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
@@ -113,19 +113,49 @@ determine_hash_delay(uint32_t key_id)
(unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
LCL_ReadRawTime(&after);
usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec);
diff = UTI_DiffTimespecsToDouble(&after, &before);
if (i == 0 || usecs < min_usecs) {
min_usecs = usecs;
}
if (i == 0 || min_diff > diff)
min_diff = diff;
}
/* Add on a bit extra to allow for copying, conversions etc */
min_usecs += min_usecs >> 4;
nsecs = 1.0625e9 * min_diff;
DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %ld useconds", key_id, min_usecs);
DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
return min_usecs;
return nsecs;
}
/* ================================================== */
/* Decode password encoded in ASCII or HEX */
static int
decode_password(char *key)
{
int i, j, len = strlen(key);
char buf[3], *p;
if (!strncmp(key, "ASCII:", 6)) {
memmove(key, key + 6, len - 6);
return len - 6;
} else if (!strncmp(key, "HEX:", 4)) {
if ((len - 4) % 2)
return 0;
for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
key[i] = strtol(buf, &p, 16);
if (p != buf + 2)
return 0;
}
return i;
} else {
/* assume ASCII */
return len;
}
}
/* ================================================== */
@@ -170,7 +200,7 @@ KEY_Reload(void)
in = fopen(key_file, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_Keys, "Could not open keyfile %s", key_file);
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
return;
}
@@ -182,19 +212,19 @@ KEY_Reload(void)
continue;
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
LOG(LOGS_WARN, LOGF_Keys, "Could not parse key at line %d in file %s", line_number, key_file);
LOG(LOGS_WARN, "Could not parse key at line %d in file %s", line_number, key_file);
continue;
}
key.hash_id = HSH_GetHashId(hashname);
if (key.hash_id < 0) {
LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %"PRIu32, key_id);
LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id);
continue;
}
key.len = UTI_DecodePasswordFromText(keyval);
key.len = decode_password(keyval);
if (!key.len) {
LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id);
LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id);
continue;
}
@@ -214,7 +244,7 @@ KEY_Reload(void)
/* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id)
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
}
/* Erase any passwords from stack */
@@ -292,6 +322,22 @@ KEY_GetAuthDelay(uint32_t key_id)
/* ================================================== */
int
KEY_GetAuthLength(uint32_t key_id)
{
unsigned char buf[MAX_HASH_LENGTH];
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
}
/* ================================================== */
int
KEY_CheckKeyLength(uint32_t key_id)
{
@@ -307,6 +353,31 @@ KEY_CheckKeyLength(uint32_t key_id)
/* ================================================== */
static int
generate_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
{
return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
}
/* ================================================== */
static int
check_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
}
/* ================================================== */
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
@@ -318,15 +389,15 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
return UTI_GenerateNTPAuth(key->hash_id, (unsigned char *)key->val,
key->len, data, data_len, auth, auth_len);
return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
data, data_len, auth, auth_len);
}
/* ================================================== */
int
KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len)
const unsigned char *auth, int auth_len, int trunc_len)
{
Key *key;
@@ -335,6 +406,6 @@ KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
if (!key)
return 0;
return UTI_CheckNTPAuth(key->hash_id, (unsigned char *)key->val,
key->len, data, data_len, auth, auth_len);
return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
data, data_len, auth, auth_len, trunc_len);
}

5
keys.h
View File

@@ -37,11 +37,12 @@ extern void KEY_Reload(void);
extern int KEY_GetKey(uint32_t key_id, char **key, int *len);
extern int KEY_KeyKnown(uint32_t key_id);
extern int KEY_GetAuthDelay(uint32_t key_id);
extern int KEY_GetAuthLength(uint32_t key_id);
extern int KEY_CheckKeyLength(uint32_t key_id);
extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data,
int data_len, unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data,
int data_len, const unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len);
#endif /* GOT_KEYS_H */

105
local.c
View File

@@ -106,40 +106,47 @@ static double max_clock_error;
under 1s of busy waiting. */
#define NITERS 100
#define NSEC_PER_SEC 1000000000
static void
calculate_sys_precision(void)
{
struct timeval tv, old_tv;
int dusec, best_dusec;
int iters;
struct timespec ts, old_ts;
int iters, diff, best;
gettimeofday(&old_tv, NULL);
best_dusec = 1000000; /* Assume we must be better than a second */
LCL_ReadRawTime(&old_ts);
/* Assume we must be better than a second */
best = NSEC_PER_SEC;
iters = 0;
do {
gettimeofday(&tv, NULL);
dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
old_tv = tv;
if (dusec > 0) {
if (dusec < best_dusec) {
best_dusec = dusec;
}
LCL_ReadRawTime(&ts);
diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
old_ts = ts;
if (diff > 0) {
if (diff < best)
best = diff;
iters++;
}
} while (iters < NITERS);
assert(best_dusec > 0);
assert(best > 0);
precision_quantum = best_dusec * 1.0e-6;
precision_quantum = 1.0e-9 * best;
/* Get rounded log2 value of the measured precision */
precision_log = 0;
while (best_dusec < 707107) {
while (best < 707106781) {
precision_log--;
best_dusec *= 2;
best *= 2;
}
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
}
/* ================================================== */
@@ -278,7 +285,7 @@ LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
/* ================================================== */
static void
invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked,
invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
double dfreq, double doffset,
LCL_ChangeType change_type)
{
@@ -345,23 +352,29 @@ void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void
}
/* ================================================== */
/* At the moment, this is just gettimeofday(), because
I can't think of a Unix system where it would not be */
void
LCL_ReadRawTime(struct timeval *result)
LCL_ReadRawTime(struct timespec *ts)
{
if (gettimeofday(result, NULL) < 0) {
LOG_FATAL(LOGF_Local, "gettimeofday() failed");
}
#if HAVE_CLOCK_GETTIME
if (clock_gettime(CLOCK_REALTIME, ts) < 0)
LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
#else
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
UTI_TimevalToTimespec(&tv, ts);
#endif
}
/* ================================================== */
void
LCL_ReadCookedTime(struct timeval *result, double *err)
LCL_ReadCookedTime(struct timespec *result, double *err)
{
struct timeval raw;
struct timespec raw;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, result, err);
@@ -370,18 +383,18 @@ LCL_ReadCookedTime(struct timeval *result, double *err)
/* ================================================== */
void
LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err)
LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
{
double correction;
LCL_GetOffsetCorrection(raw, &correction, err);
UTI_AddDoubleToTimeval(raw, correction, cooked);
UTI_AddDoubleToTimespec(raw, correction, cooked);
}
/* ================================================== */
void
LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err)
LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
{
/* Call system specific driver to get correction */
(*drv_offset_convert)(raw, correction, err);
@@ -413,7 +426,7 @@ clamp_freq(double freq)
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
return freq;
LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq);
LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
}
@@ -421,13 +434,13 @@ clamp_freq(double freq)
/* ================================================== */
static int
check_offset(struct timeval *now, double offset)
check_offset(struct timespec *now, double offset)
{
/* Check if the time will be still sane with accumulated offset */
if (UTI_IsTimeOffsetSane(now, -offset))
return 1;
LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset);
LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
return 0;
}
@@ -439,7 +452,7 @@ check_offset(struct timeval *now, double offset)
void
LCL_SetAbsoluteFrequency(double afreq_ppm)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
double dfreq;
afreq_ppm = clamp_freq(afreq_ppm);
@@ -470,7 +483,7 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
void
LCL_AccumulateDeltaFrequency(double dfreq)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
double old_freq_ppm;
old_freq_ppm = current_freq_ppm;
@@ -499,7 +512,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
void
LCL_AccumulateOffset(double offset, double corr_rate)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */
@@ -521,7 +534,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
int
LCL_ApplyStepOffset(double offset)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */
@@ -533,7 +546,7 @@ LCL_ApplyStepOffset(double offset)
return 0;
if (!(*drv_apply_step_offset)(offset)) {
LOG(LOGS_ERR, LOGF_Local, "Could not step clock");
LOG(LOGS_ERR, "Could not step system clock");
return 0;
}
@@ -549,7 +562,7 @@ LCL_ApplyStepOffset(double offset)
/* ================================================== */
void
LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion)
{
/* Dispatch to all handlers */
@@ -563,7 +576,7 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
void
LCL_NotifyLeap(int leap)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
@@ -580,7 +593,7 @@ LCL_NotifyLeap(int leap)
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
struct timeval raw, cooked;
struct timespec raw, cooked;
double old_freq_ppm;
LCL_ReadRawTime(&raw);
@@ -600,7 +613,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
current_freq_ppm = clamp_freq(current_freq_ppm);
DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
old_freq_ppm, current_freq_ppm, doffset);
/* Call the system-specific driver for setting the frequency */
@@ -647,7 +660,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
current_freq_ppm = (*drv_read_freq)();
DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
}
/* ================================================== */
@@ -657,7 +670,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
int
LCL_MakeStep(void)
{
struct timeval raw;
struct timespec raw;
double correction;
LCL_ReadRawTime(&raw);
@@ -671,7 +684,7 @@ LCL_MakeStep(void)
if (!LCL_ApplyStepOffset(-correction))
return 0;
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
return 1;
}
@@ -687,10 +700,10 @@ LCL_CanSystemLeap(void)
/* ================================================== */
void
LCL_SetSystemLeap(int leap)
LCL_SetSystemLeap(int leap, int tai_offset)
{
if (drv_set_leap) {
(drv_set_leap)(leap);
(drv_set_leap)(leap, tai_offset);
}
}

24
local.h
View File

@@ -31,9 +31,8 @@
#include "sysincl.h"
/* Read the system clock. This is analogous to gettimeofday(),
but with the timezone information ignored */
extern void LCL_ReadRawTime(struct timeval *);
/* Read the system clock */
extern void LCL_ReadRawTime(struct timespec *ts);
/* Read the system clock, corrected according to all accumulated
drifts and uncompensated offsets.
@@ -44,15 +43,15 @@ extern void LCL_ReadRawTime(struct timeval *);
adjtime()-like interface to correct offsets, and to adjust the
frequency), we must correct the raw time to get this value */
extern void LCL_ReadCookedTime(struct timeval *t, double *err);
extern void LCL_ReadCookedTime(struct timespec *ts, double *err);
/* Convert raw time to cooked. */
extern void LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err);
extern void LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err);
/* Read the current offset between the system clock and true time
(i.e. 'cooked' - 'raw') (in seconds). */
extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err);
extern void LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err);
/* Type of routines that may be invoked as callbacks when there is a
change to the frequency or offset.
@@ -79,7 +78,7 @@ typedef enum {
} LCL_ChangeType;
typedef void (*LCL_ParameterChangeHandler)
(struct timeval *raw, struct timeval *cooked,
(struct timespec *raw, struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -163,7 +162,7 @@ extern int LCL_ApplyStepOffset(double offset);
/* Routine to invoke notify handlers on an unexpected time jump
in system clock */
extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
extern void LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion);
/* Routine to invoke notify handlers on leap second when the system clock
@@ -202,10 +201,11 @@ extern int LCL_MakeStep(void);
does something */
extern int LCL_CanSystemLeap(void);
/* Routine to set the system clock to correct itself for a leap second if
supported. Leap second will be inserted at the end of the day if the
argument is positive, deleted if negative, and zero resets the setting. */
extern void LCL_SetSystemLeap(int leap);
/* Routine to set the system clock to correct itself for a leap second and also
set its TAI-UTC offset. If supported, leap second will be inserted at the
end of the day if the argument is positive, deleted if negative, and zero
resets the setting. */
extern void LCL_SetSystemLeap(int leap, int tai_offset);
/* Routine to set a frequency correction (in ppm) that should be applied
to local clock to compensate for temperature changes. A positive

View File

@@ -52,10 +52,10 @@ typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
/* System driver to convert a raw time to an adjusted (cooked) time.
The number of seconds returned in 'corr' have to be added to the
raw time to get the corrected time */
typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr, double *err);
typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err);
/* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap);
/* System driver to schedule leap seconds and set TAI-UTC offset */
typedef void (*lcl_SetLeapDriver)(int leap, int tai_offset);
/* System driver to set the synchronisation status */
typedef void (*lcl_SetSyncStatusDriver)(int synchronised, double est_error, double max_error);

View File

@@ -40,6 +40,7 @@ int log_debug_enabled = 0;
/* Flag indicating we have initialised */
static int initialised = 0;
static FILE *file_log;
static int system_log = 0;
static int parent_fd = 0;
@@ -69,6 +70,7 @@ void
LOG_Initialise(void)
{
initialised = 1;
file_log = stderr;
}
/* ================================================== */
@@ -79,6 +81,8 @@ LOG_Finalise(void)
{
if (system_log) {
closelog();
} else {
fclose(file_log);
}
LOG_CycleLogFiles();
@@ -113,7 +117,7 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
}
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else {
fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message);
fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
}
@@ -121,8 +125,7 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
void LOG_Message(LOG_Severity severity,
#if DEBUG > 0
LOG_Facility facility, int line_number,
const char *filename, const char *function_name,
int line_number, const char *filename, const char *function_name,
#endif
const char *format, ...)
{
@@ -136,10 +139,10 @@ void LOG_Message(LOG_Severity severity,
time(&t);
stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
fprintf(stderr, "%s ", buf);
fprintf(file_log, "%s ", buf);
#if DEBUG > 0
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name);
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
#endif
}
@@ -174,6 +177,21 @@ void LOG_Message(LOG_Severity severity,
}
}
/* ================================================== */
void
LOG_OpenFileLog(const char *log_file)
{
FILE *f;
f = fopen(log_file, "a");
if (!f)
LOG_FATAL("Could not open log file %s", log_file);
file_log = f;
}
/* ================================================== */
void
@@ -238,12 +256,18 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return;
if (!logfiles[id].file) {
char filename[512];
char filename[512], *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') {
LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL;
return;
}
if (snprintf(filename, sizeof(filename), "%s/%s.log",
CNF_GetLogDir(), logfiles[id].name) >= sizeof(filename) ||
logdir, logfiles[id].name) >= sizeof (filename) ||
!(logfiles[id].file = fopen(filename, "a"))) {
LOG(LOGS_WARN, LOGF_Refclock, "Couldn't open logfile %s for update", filename);
LOG(LOGS_WARN, "Could not open log file %s", filename);
logfiles[id].name = NULL;
return;
}

View File

@@ -46,26 +46,26 @@ extern int log_debug_enabled;
#endif
#if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__);
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#else
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, __VA_ARGS__);
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __VA_ARGS__)
#endif
#define DEBUG_LOG(facility, ...) \
#define DEBUG_LOG(...) \
do { \
if (DEBUG && log_debug_enabled) \
LOG_MESSAGE(LOGS_DEBUG, facility, __VA_ARGS__); \
LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
} while (0)
#define LOG_FATAL(facility, ...) \
#define LOG_FATAL(...) \
do { \
LOG_MESSAGE(LOGS_FATAL, facility, __VA_ARGS__); \
LOG_MESSAGE(LOGS_FATAL, __VA_ARGS__); \
exit(1); \
} while (0)
#define LOG(severity, facility, ...) LOG_MESSAGE(severity, facility, __VA_ARGS__)
#define LOG(severity, ...) LOG_MESSAGE(severity, __VA_ARGS__)
/* Definition of severity */
typedef enum {
@@ -76,47 +76,6 @@ typedef enum {
LOGS_DEBUG
} LOG_Severity;
/* Definition of facility. Each message is tagged with who generated
it, so that the user can customise what level of reporting he gets
for each area of the software */
typedef enum {
LOGF_Reference,
LOGF_NtpIO,
LOGF_NtpCore,
LOGF_NtpSources,
LOGF_Scheduler,
LOGF_SourceStats,
LOGF_Sources,
LOGF_Local,
LOGF_Util,
LOGF_Main,
LOGF_Memory,
LOGF_Client,
LOGF_ClientLog,
LOGF_Configure,
LOGF_CmdMon,
LOGF_Acquire,
LOGF_Manual,
LOGF_Keys,
LOGF_Logging,
LOGF_Nameserv,
LOGF_PrivOps,
LOGF_Rtc,
LOGF_Regress,
LOGF_Sys,
LOGF_SysGeneric,
LOGF_SysLinux,
LOGF_SysMacOSX,
LOGF_SysNetBSD,
LOGF_SysSolaris,
LOGF_SysTimex,
LOGF_SysWinnt,
LOGF_TempComp,
LOGF_RtcLinux,
LOGF_Refclock,
LOGF_Smooth,
} LOG_Facility;
/* Init function */
extern void LOG_Initialise(void);
@@ -125,9 +84,8 @@ extern void LOG_Finalise(void);
/* Line logging function */
#if DEBUG > 0
FORMAT_ATTRIBUTE_PRINTF(6, 7)
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename,
FORMAT_ATTRIBUTE_PRINTF(5, 6)
extern void LOG_Message(LOG_Severity severity, int line_number, const char *filename,
const char *function_name, const char *format, ...);
#else
FORMAT_ATTRIBUTE_PRINTF(2, 3)
@@ -141,6 +99,9 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
*/
extern void LOG_SetDebugLevel(int level);
/* Log messages to a file instead of stderr */
extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void);

296
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2015
* Copyright (C) Miroslav Lichvar 2012-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -35,6 +35,7 @@
#include "local.h"
#include "sys.h"
#include "ntp_io.h"
#include "ntp_signd.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "sources.h"
@@ -85,6 +86,10 @@ static void
delete_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
if (!pidfile[0])
return;
/* Don't care if this fails, there's not a lot we can do */
unlink(pidfile);
}
@@ -96,7 +101,7 @@ MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
if (CNF_GetDumpOnExit()) {
if (CNF_GetDumpDir()[0] != '\0') {
SRC_DumpSources();
}
@@ -107,6 +112,7 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
NSD_Finalise();
NSR_Finalise();
SST_Finalise();
NCR_Finalise();
@@ -125,9 +131,8 @@ MAI_CleanupAndExit(void)
delete_pidfile();
CNF_Finalise();
LOG_Finalise();
HSH_Finalise();
LOG_Finalise();
exit(exit_status);
}
@@ -143,6 +148,16 @@ signal_cleanup(int x)
/* ================================================== */
static void
quit_timeout(void *arg)
{
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();
}
/* ================================================== */
static void
ntp_source_resolving_end(void)
{
@@ -156,6 +171,7 @@ ntp_source_resolving_end(void)
SRC_ReloadSources();
}
SRC_RemoveDumpFiles();
RTC_StartMeasurements();
RCL_StartRefclocks();
NSR_StartSources();
@@ -229,55 +245,45 @@ post_init_rtc_hook(void *anything)
}
/* ================================================== */
/* Return 1 if the process exists on the system. */
static int
does_process_exist(int pid)
{
int status;
status = getsid(pid);
if (status >= 0) {
return 1;
} else {
return 0;
}
}
/* ================================================== */
static int
maybe_another_chronyd_running(int *other_pid)
static void
check_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
FILE *in;
int pid, count;
*other_pid = 0;
in = fopen(pidfile, "r");
if (!in) return 0;
if (!in)
return;
count = fscanf(in, "%d", &pid);
fclose(in);
if (count != 1) return 0;
if (count != 1)
return;
*other_pid = pid;
return does_process_exist(pid);
if (getsid(pid) < 0)
return;
LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
pid, pidfile);
}
/* ================================================== */
static void
write_lockfile(void)
write_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
FILE *out;
if (!pidfile[0])
return;
out = fopen(pidfile, "w");
if (!out) {
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno));
} else {
fprintf(out, "%d\n", (int)getpid());
fclose(out);
@@ -294,14 +300,14 @@ go_daemon(void)
/* Create pipe which will the daemon use to notify the grandparent
when it's initialised or send an error message */
if (pipe(pipefd)) {
LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno));
LOG_FATAL("pipe() failed : %s", strerror(errno));
}
/* Does this preserve existing signal handlers? */
pid = fork();
if (pid < 0) {
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
/* In the 'grandparent' */
char message[1024];
@@ -312,7 +318,8 @@ go_daemon(void)
if (r) {
if (r > 0) {
/* Print the error message from the child */
fprintf(stderr, "%.1024s\n", message);
message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
}
exit(1);
} else
@@ -326,7 +333,7 @@ go_daemon(void)
pid = fork();
if (pid < 0) {
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
exit(0); /* In the 'parent' */
} else {
@@ -334,7 +341,7 @@ go_daemon(void)
/* Change current directory to / */
if (chdir("/") < 0) {
LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
LOG_FATAL("chdir() failed : %s", strerror(errno));
}
/* Don't keep stdin/out/err from before. But don't close
@@ -351,138 +358,171 @@ go_daemon(void)
/* ================================================== */
static void
print_help(const char *progname)
{
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
}
/* ================================================== */
static void
print_version(void)
{
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
}
/* ================================================== */
static int
parse_int_arg(const char *arg)
{
int i;
if (sscanf(arg, "%d", &i) != 1)
LOG_FATAL("Invalid argument %s", arg);
return i;
}
/* ================================================== */
int main
(int argc, char **argv)
{
const char *conf_file = DEFAULT_CONF_FILE;
const char *progname = argv[0];
char *user = NULL;
char *user = NULL, *log_file = NULL;
struct passwd *pw;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0;
int other_pid;
int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int system_log = 1;
int clock_control = 1, system_log = 1;
int config_args = 0;
do_platform_checks();
LOG_Initialise();
/* Parse command line options */
while (++argv, (--argc)>0) {
if (!strcmp("-f", *argv)) {
++argv, --argc;
conf_file = *argv;
} else if (!strcmp("-P", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) {
LOG_FATAL(LOGF_Main, "Bad scheduler priority");
}
} else if (!strcmp("-m", *argv)) {
lock_memory = 1;
} else if (!strcmp("-r", *argv)) {
reload = 1;
} else if (!strcmp("-R", *argv)) {
restarted = 1;
} else if (!strcmp("-u", *argv)) {
++argv, --argc;
if (argc == 0) {
LOG_FATAL(LOGF_Main, "Missing user name");
} else {
user = *argv;
}
} else if (!strcmp("-F", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &scfilter_level) != 1)
LOG_FATAL(LOGF_Main, "Bad syscall filter level");
} else if (!strcmp("-s", *argv)) {
do_init_rtc = 1;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
/* Parse (undocumented) long command-line options */
for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) {
print_help(progname);
return 0;
} else if (!strcmp("-n", *argv)) {
nofork = 1;
} else if (!strcmp("-d", *argv)) {
debug++;
nofork = 1;
system_log = 0;
} else if (!strcmp("-q", *argv)) {
ref_mode = REF_ModeUpdateOnce;
nofork = 1;
system_log = 0;
} else if (!strcmp("-Q", *argv)) {
ref_mode = REF_ModePrintOnce;
nofork = 1;
system_log = 0;
} else if (!strcmp("-4", *argv)) {
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n",
progname);
} else if (!strcmp("--version", argv[optind])) {
print_version();
return 0;
} else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else {
/* Process remaining arguments and configuration lines */
config_args = argc;
break;
}
}
if (getuid() != 0) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
fprintf(stderr,"Not superuser\n");
return 1;
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
switch (opt) {
case '4':
case '6':
address_family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
break;
case 'd':
debug++;
nofork = 1;
system_log = 0;
break;
case 'f':
conf_file = optarg;
break;
case 'F':
scfilter_level = parse_int_arg(optarg);
break;
case 'l':
log_file = optarg;
break;
case 'm':
lock_memory = 1;
break;
case 'n':
nofork = 1;
break;
case 'P':
sched_priority = parse_int_arg(optarg);
break;
case 'q':
case 'Q':
ref_mode = opt == 'q' ? REF_ModeUpdateOnce : REF_ModePrintOnce;
nofork = 1;
client_only = 1;
clock_control = 0;
system_log = 0;
break;
case 'r':
reload = 1;
break;
case 'R':
restarted = 1;
break;
case 's':
do_init_rtc = 1;
break;
case 't':
timeout = parse_int_arg(optarg);
break;
case 'u':
user = optarg;
break;
case 'v':
print_version();
return 0;
case 'x':
clock_control = 0;
break;
default:
print_help(progname);
return opt != 'h';
}
}
if (getuid() && !client_only)
LOG_FATAL("Not superuser");
/* Turn into a daemon */
if (!nofork) {
go_daemon();
}
if (system_log) {
if (log_file) {
LOG_OpenFileLog(log_file);
} else if (system_log) {
LOG_OpenSystemLog();
}
LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)",
CHRONY_VERSION, CHRONYD_FEATURES);
LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted);
CNF_Initialise(restarted, client_only);
/* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
if (!config_args) {
CNF_ReadFile(conf_file);
} else {
do {
CNF_ParseLine(NULL, config_args - argc + 1, *argv);
} while (++argv, --argc);
for (; optind < argc; optind++)
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
}
/* Check whether another chronyd may already be running. Do this after
* forking, so that message logging goes to the right place (i.e. syslog), in
* case this chronyd is being run from a boot script. */
if (maybe_another_chronyd_running(&other_pid)) {
LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)",
other_pid, CNF_GetPidFile());
}
/* Check whether another chronyd may already be running */
check_pidfile();
/* Write our lockfile to prevent other chronyds running. This has *GOT* to
* be done *AFTER* the daemon-creation fork() */
write_lockfile();
/* Write our pidfile to prevent other chronyds running */
write_pidfile();
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
SYS_Initialise();
SYS_Initialise(clock_control);
RTC_Initialise(do_init_rtc);
SRC_Initialise();
RCL_Initialise();
@@ -511,18 +551,19 @@ int main
}
if ((pw = getpwnam(user)) == NULL)
LOG_FATAL(LOGF_Main, "Could not get %s uid/gid", user);
LOG_FATAL("Could not get %s uid/gid", user);
/* Create all directories before dropping root */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Drop root privileges if the user has non-zero uid or gid */
if (pw->pw_uid || pw->pw_gid)
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
REF_Initialise();
SST_Initialise();
NSR_Initialise();
NSD_Initialise();
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();
@@ -545,6 +586,9 @@ int main
REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode);
if (timeout > 0)
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
if (do_init_rtc) {
RTC_TimeInit(post_init_rtc_hook, NULL);
} else {
@@ -555,7 +599,7 @@ int main
the scheduler. */
SCH_MainLoop();
LOG(LOGS_INFO, LOGF_Main, "chronyd exiting");
LOG(LOGS_INFO, "chronyd exiting");
MAI_CleanupAndExit();

View File

@@ -47,7 +47,7 @@ static int enabled = 0;
/* More recent samples at highest indices */
typedef struct {
struct timeval when; /* This is our 'cooked' time */
struct timespec when; /* This is our 'cooked' time */
double orig_offset; /*+ Not modified by slew samples */
double offset; /*+ if we are fast of the supplied reference */
double residual; /*+ regression residual (sign convention given by
@@ -64,8 +64,8 @@ static int n_samples;
/* ================================================== */
static void
slew_samples(struct timeval *raw,
struct timeval *cooked,
slew_samples(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -97,7 +97,8 @@ MNL_Finalise(void)
/* ================================================== */
static void
estimate_and_set_system(struct timeval *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
estimate_and_set_system(struct timespec *now, int offset_provided, double offset,
double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
{
double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
double b0, b1;
@@ -108,32 +109,26 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
int found_freq;
double slew_by;
b0 = offset_provided ? offset : 0.0;
b1 = freq = 0.0;
found_freq = 0;
if (n_samples > 1) {
for (i=0; i<n_samples; i++) {
UTI_DiffTimevalsToDouble(&agos[i], &samples[n_samples-1].when, &samples[i].when);
agos[i] = UTI_DiffTimespecsToDouble(&samples[n_samples - 1].when, &samples[i].when);
offsets[i] = samples[i].offset;
}
RGR_FindBestRobustRegression(agos, offsets, n_samples,
1.0e-8, /* 0.01ppm easily good enough for this! */
&b0, &b1, &n_runs, &best_start);
/* Ignore b0 from regression; treat offset as being the most
recently entered value. (If the administrator knows he's put
an outlier in, he will rerun the settime operation.) However,
the frequency estimate comes from the regression. */
freq = -b1;
found_freq = 1;
} else {
if (offset_provided) {
b0 = offset;
} else {
b0 = 0.0;
if (RGR_FindBestRobustRegression(agos, offsets, n_samples, 1.0e-8,
&b0, &b1, &n_runs, &best_start)) {
/* Ignore b0 from regression; treat offset as being the most
recently entered value. (If the administrator knows he's put
an outlier in, he will rerun the settime operation.) However,
the frequency estimate comes from the regression. */
freq = -b1;
found_freq = 1;
}
b1 = freq = 0.0;
found_freq = 0;
} else {
agos[0] = 0.0;
offsets[0] = b0;
}
@@ -145,21 +140,20 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
}
if (found_freq) {
LOG(LOGS_INFO, LOGF_Manual,
"Making a frequency change of %.3f ppm and a slew of %.6f",
LOG(LOGS_INFO, "Making a frequency change of %.3f ppm and a slew of %.6f",
1.0e6 * freq, slew_by);
REF_SetManualReference(now,
slew_by,
freq, skew);
} else {
LOG(LOGS_INFO, LOGF_Manual, "Making a slew of %.6f", slew_by);
LOG(LOGS_INFO, "Making a slew of %.6f", slew_by);
REF_SetManualReference(now,
slew_by,
0.0, skew);
}
if (offset_cs) *offset_cs = (long)(0.5 + 100.0 * b0);
if (reg_offset) *reg_offset = b0;
if (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq;
if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency();
@@ -173,9 +167,9 @@ estimate_and_set_system(struct timeval *now, int offset_provided, double offset,
/* ================================================== */
int
MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
{
struct timeval now;
struct timespec now;
double offset, diff;
int i;
@@ -189,12 +183,12 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
return 0;
if (n_samples) {
UTI_DiffTimevalsToDouble(&diff, &now, &samples[n_samples - 1].when);
diff = UTI_DiffTimespecsToDouble(&now, &samples[n_samples - 1].when);
if (diff < MIN_SAMPLE_SEPARATION)
return 0;
}
UTI_DiffTimevalsToDouble(&offset, &now, ts);
offset = UTI_DiffTimespecsToDouble(&now, ts);
/* Check if buffer full up */
if (n_samples == MAX_SAMPLES) {
@@ -210,7 +204,7 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
samples[n_samples].orig_offset = offset;
++n_samples;
estimate_and_set_system(&now, 1, offset, offset_cs, dfreq_ppm, new_afreq_ppm);
estimate_and_set_system(&now, 1, offset, reg_offset, dfreq_ppm, new_afreq_ppm);
return 1;
@@ -224,8 +218,8 @@ MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, doub
/* ================================================== */
static void
slew_samples(struct timeval *raw,
struct timeval *cooked,
slew_samples(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -239,7 +233,7 @@ slew_samples(struct timeval *raw,
}
for (i=0; i<n_samples; i++) {
UTI_AdjustTimeval(&samples[i].when, cooked, &samples[i].when, &delta_time,
UTI_AdjustTimespec(&samples[i].when, cooked, &samples[i].when, &delta_time,
dfreq, doffset);
samples[i].offset += delta_time;
}
@@ -309,7 +303,7 @@ int
MNL_DeleteSample(int index)
{
int i;
struct timeval now;
struct timespec now;
if ((index < 0) || (index >= n_samples)) {
return 0;

View File

@@ -33,7 +33,7 @@
extern void MNL_Initialise(void);
extern void MNL_Finalise(void);
extern int MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
extern int MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm);
extern void MNL_Enable(void);
extern void MNL_Disable(void);

View File

@@ -37,7 +37,7 @@ Malloc(size_t size)
r = malloc(size);
if (!r && size)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}
@@ -49,7 +49,7 @@ Realloc(void *ptr, size_t size)
r = realloc(ptr, size);
if (!r && size)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}
@@ -61,7 +61,7 @@ Strdup(const char *s)
r = strdup(s);
if (!r)
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
LOG_FATAL("Could not allocate memory");
return r;
}

View File

@@ -72,18 +72,18 @@ start_resolving(void *anything)
/* ================================================== */
static void
end_resolving(void *anything)
end_resolving(int fd, int event, void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
int i;
if (pthread_join(inst->thread, NULL)) {
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
LOG_FATAL("pthread_join() failed");
}
resolving_threads--;
SCH_RemoveInputFileHandler(inst->pipe[0]);
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
@@ -110,17 +110,20 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->status = DNS_Failure;
if (pipe(inst->pipe)) {
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
LOG_FATAL("pipe() failed");
}
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
resolving_threads++;
assert(resolving_threads <= 1);
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
LOG_FATAL("pthread_create() failed");
}
SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst);
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst);
}
/* ================================================== */

7
ntp.h
View File

@@ -38,6 +38,9 @@ typedef struct {
typedef uint32_t NTP_int32;
/* The UDP port number used by NTP */
#define NTP_PORT 123
/* The NTP protocol version that we support */
#define NTP_VERSION 4
@@ -56,6 +59,10 @@ typedef uint32_t NTP_int32;
#define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
/* The maximum length of MAC in NTPv4 packets which allows deterministic
parsing of extension fields (RFC 7822) */
#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
/* Type definition for leap bits */
typedef enum {
LEAP_Normal = 0,

1212
ntp_core.c

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,18 @@ typedef enum {
NTP_SERVER, NTP_PEER
} NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct {
struct timespec ts;
double err;
NTP_Timestamp_Source source;
} NTP_Local_Timestamp;
/* This is a private data type used for storing the instance record for
each source that we are chiming with */
typedef struct NCR_Instance_Record *NCR_Instance;
@@ -58,19 +70,34 @@ extern void NCR_StartInstance(NCR_Instance instance);
/* Reset an instance */
extern void NCR_ResetInstance(NCR_Instance inst);
/* Reset polling interval of an instance */
extern void NCR_ResetPoll(NCR_Instance instance);
/* Change the remote address of an instance */
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
/* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */
extern int NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, NTP_Local_Address *local_addr, int length);
extern int NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a new packet arrives off the network,
and we do not recognize its source */
extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
extern void NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a source we have
an ongoing protocol exchange with */
extern void NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a destination we
do not recognize */
extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);
extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
/* Take a particular source online (i.e. start sampling it) */
extern void NCR_TakeSourceOnline(NCR_Instance inst);
@@ -95,7 +122,8 @@ extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now);
extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
extern int NCR_CheckAccessRestriction(IPAddr *ip_addr);

378
ntp_io.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2015
* Copyright (C) Miroslav Lichvar 2009, 2013-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -30,6 +30,7 @@
#include "sysincl.h"
#include "array.h"
#include "ntp_io.h"
#include "ntp_core.h"
#include "ntp_sources.h"
@@ -40,7 +41,12 @@
#include "privops.h"
#include "util.h"
#ifdef HAVE_LINUX_TIMESTAMPING
#include "ntp_io_linux.h"
#endif
#define INVALID_SOCK_FD -1
#define CMSGBUF_SIZE 256
union sockaddr_in46 {
struct sockaddr_in in4;
@@ -50,6 +56,31 @@ union sockaddr_in46 {
struct sockaddr u;
};
struct Message {
union sockaddr_in46 name;
struct iovec iov;
NTP_Receive_Buffer buf;
/* Aligned buffer for control messages */
struct cmsghdr cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
};
#ifdef HAVE_RECVMMSG
#define MAX_RECV_MESSAGES 4
#define MessageHeader mmsghdr
#else
/* Compatible with mmsghdr */
struct MessageHeader {
struct msghdr msg_hdr;
unsigned int msg_len;
};
#define MAX_RECV_MESSAGES 1
#endif
/* Arrays of Message and MessageHeader */
static ARR_Instance recv_messages;
static ARR_Instance recv_headers;
/* The server/peer and client sockets for IPv4 and IPv6 */
static int server_sock_fd4;
static int client_sock_fd4;
@@ -80,7 +111,7 @@ static int initialised=0;
/* ================================================== */
/* Forward prototypes */
static void read_from_socket(void *anything);
static void read_from_socket(int sock_fd, int event, void *anything);
/* ================================================== */
@@ -91,18 +122,18 @@ prepare_socket(int family, int port_number, int client_only)
socklen_t my_addr_len;
int sock_fd;
IPAddr bind_address;
int on_off = 1;
int events = SCH_FILE_INPUT, on_off = 1;
/* Open Internet domain UDP socket for NTP message transmissions */
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
if (!client_only) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
LOG(LOGS_ERR, "Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
} else {
DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s",
DEBUG_LOG("Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
}
return INVALID_SOCK_FD;
@@ -162,30 +193,35 @@ prepare_socket(int family, int port_number, int client_only)
/* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_REUSEADDR");
LOG(LOGS_ERR, "Could not set %s socket option", "SO_REUSEADDR");
/* Don't quit - we might survive anyway */
}
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_BROADCAST");
LOG(LOGS_ERR, "Could not set %s socket option", "SO_BROADCAST");
/* Don't quit - we might survive anyway */
}
#ifdef SO_TIMESTAMP
/* Enable receiving of timestamp control messages */
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP");
/* Don't quit - we might survive anyway */
}
/* Enable kernel/HW timestamping of packets */
#ifdef HAVE_LINUX_TIMESTAMPING
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
#endif
#ifdef SO_TIMESTAMPNS
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
#endif
#ifdef SO_TIMESTAMP
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP");
#endif
;
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_FREEBIND");
LOG(LOGS_ERR, "Could not set %s socket option", "IP_FREEBIND");
}
#endif
@@ -193,7 +229,7 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef HAVE_IN_PKTINFO
/* We want the local IP info on server sockets */
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_PKTINFO");
LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO");
/* Don't quit - we might survive anyway */
}
#endif
@@ -203,18 +239,18 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_V6ONLY");
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_V6ONLY");
}
#endif
#ifdef HAVE_IN6_PKTINFO
#ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_RECVPKTINFO");
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_RECVPKTINFO");
}
#else
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_PKTINFO");
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_PKTINFO");
}
#endif
#endif
@@ -223,14 +259,14 @@ prepare_socket(int family, int port_number, int client_only)
/* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
LOG(LOGS_ERR, "Could not bind %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd);
return INVALID_SOCK_FD;
}
/* Register handler for read events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
/* Register handler for read and possibly exception events on the socket */
SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
return sock_fd;
}
@@ -265,7 +301,7 @@ connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
assert(addr_len);
if (connect(sock_fd, &addr.u, addr_len) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
DEBUG_LOG("Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
@@ -282,11 +318,38 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
SCH_RemoveInputFileHandler(sock_fd);
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
}
/* ================================================== */
static void
prepare_buffers(unsigned int n)
{
struct MessageHeader *hdr;
struct Message *msg;
unsigned int i;
for (i = 0; i < n; i++) {
msg = ARR_GetElement(recv_messages, i);
hdr = ARR_GetElement(recv_headers, i);
msg->iov.iov_base = &msg->buf;
msg->iov.iov_len = sizeof (msg->buf);
hdr->msg_hdr.msg_name = &msg->name;
hdr->msg_hdr.msg_namelen = sizeof (msg->name);
hdr->msg_hdr.msg_iov = &msg->iov;
hdr->msg_hdr.msg_iovlen = 1;
hdr->msg_hdr.msg_control = &msg->cmsgbuf;
hdr->msg_hdr.msg_controllen = sizeof (msg->cmsgbuf);
hdr->msg_hdr.msg_flags = 0;
hdr->msg_len = 0;
}
}
/* ================================================== */
void
NIO_Initialise(int family)
{
@@ -295,6 +358,22 @@ NIO_Initialise(int family)
assert(!initialised);
initialised = 1;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise();
#else
if (1) {
CNF_HwTsInterface *conf_iface;
if (CNF_GetHwTsInterface(0, &conf_iface))
LOG_FATAL("HW timestamping not supported");
}
#endif
recv_messages = ARR_CreateInstance(sizeof (struct Message));
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
prepare_buffers(MAX_RECV_MESSAGES);
server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort();
@@ -348,7 +427,7 @@ NIO_Initialise(int family)
&& client_sock_fd6 == INVALID_SOCK_FD
#endif
)) {
LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets");
LOG_FATAL("Could not open NTP sockets");
}
}
@@ -367,6 +446,13 @@ NIO_Finalise(void)
close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#endif
ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages);
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise();
#endif
initialised = 0;
}
@@ -482,133 +568,185 @@ NIO_IsServerSocket(int sock_fd)
/* ================================================== */
static void
read_from_socket(void *anything)
process_message(struct msghdr *hdr, int length, int sock_fd)
{
/* This should only be called when there is something
to read, otherwise it will block. */
int status, sock_fd;
NTP_Receive_Buffer message;
union sockaddr_in46 where_from;
unsigned int flags = 0;
struct timeval now;
double now_err;
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
char cmsgbuf[256];
struct msghdr msg;
struct iovec iov;
NTP_Local_Timestamp local_ts;
struct timespec sched_ts;
struct cmsghdr *cmsg;
assert(initialised);
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON;
sched_ts = local_ts.ts;
SCH_GetLastEventTime(&now, &now_err, NULL);
if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
DEBUG_LOG("Truncated source address");
return;
}
iov.iov_base = &message.ntp_pkt;
iov.iov_len = sizeof(message);
msg.msg_name = &where_from;
msg.msg_namelen = sizeof(where_from);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = (void *) cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = 0;
if (hdr->msg_namelen >= sizeof (((struct sockaddr *)hdr->msg_name)->sa_family)) {
UTI_SockaddrToIPAndPort((struct sockaddr *)hdr->msg_name,
&remote_addr.ip_addr, &remote_addr.port);
} else {
remote_addr.ip_addr.family = IPADDR_UNSPEC;
remote_addr.port = 0;
}
sock_fd = (long)anything;
status = recvmsg(sock_fd, &msg, flags);
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = sock_fd;
/* Don't bother checking if read failed or why if it did. More
likely than not, it will be connection refused, resulting from a
previous sendto() directing a datagram at a port that is not
listening (which appears to generate an ICMP response, and on
some architectures e.g. Linux this is translated into an error
reponse on a subsequent recvfrom). */
if (hdr->msg_flags & MSG_TRUNC) {
DEBUG_LOG("Received truncated message from %s:%d",
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
return;
}
if (status > 0) {
if (msg.msg_namelen > sizeof (where_from))
LOG_FATAL(LOGF_NtpIO, "Truncated source address");
if (hdr->msg_flags & MSG_CTRUNC) {
DEBUG_LOG("Truncated control message");
/* Continue */
}
UTI_SockaddrToIPAndPort(&where_from.u, &remote_addr.ip_addr, &remote_addr.port);
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = sock_fd;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_IN_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi;
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
}
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
local_addr.if_index = ipi.ipi_ifindex;
}
#endif
#ifdef HAVE_IN6_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi;
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6;
}
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6;
local_addr.if_index = ipi.ipi6_ifindex;
}
#endif
#ifdef SO_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval tv;
#ifdef SCM_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval tv;
struct timespec ts;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
LCL_CookTime(&tv, &now, &now_err);
}
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
UTI_TimevalToTimespec(&tv, &ts);
LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
}
#endif
#ifdef SCM_TIMESTAMPNS
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
struct timespec ts;
memcpy(&ts, CMSG_DATA(cmsg), sizeof (ts));
LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
}
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
status, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
if (status >= NTP_NORMAL_PACKET_LENGTH) {
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
&remote_addr, &local_addr, status);
} else {
/* Just ignore the packet if it's not of a recognized length */
}
#endif
}
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length))
return;
#endif
DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
/* Just ignore the packet if it's not of a recognized length */
if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer))
return;
NSR_ProcessRx(&remote_addr, &local_addr, &local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
}
/* ================================================== */
static void
read_from_socket(int sock_fd, int event, void *anything)
{
/* This should only be called when there is something
to read, otherwise it may block */
struct MessageHeader *hdr;
unsigned int i, n;
int status, flags = 0;
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
assert(n >= 1);
if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING
flags |= MSG_ERRQUEUE;
#else
assert(0);
#endif
}
#ifdef HAVE_RECVMMSG
status = recvmmsg(sock_fd, hdr, n, flags | MSG_DONTWAIT, NULL);
if (status >= 0)
n = status;
#else
n = 1;
status = recvmsg(sock_fd, &hdr[0].msg_hdr, flags);
if (status >= 0)
hdr[0].msg_len = status;
#endif
if (status < 0) {
DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
strerror(errno));
return;
}
for (i = 0; i < n; i++) {
hdr = ARR_GetElement(recv_headers, i);
process_message(&hdr->msg_hdr, hdr->msg_len, sock_fd);
}
/* Restore the buffers to their original state */
prepare_buffers(n);
}
/* ================================================== */
/* Send a packet to remote address from local address */
static int
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx)
{
union sockaddr_in46 remote;
struct msghdr msg;
struct iovec iov;
char cmsgbuf[256];
struct cmsghdr *cmsg, cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
int cmsglen;
socklen_t addrlen = 0;
assert(initialised);
if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
DEBUG_LOG("No socket to send to %s:%d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
return 0;
}
/* Don't set address with connected socket */
if (local_addr->sock_fd == server_sock_fd4 ||
#ifdef FEAT_IPV6
local_addr->sock_fd == server_sock_fd6 ||
#endif
!separate_client_sockets) {
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port,
&remote.u);
if (!addrlen)
@@ -624,7 +762,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
}
iov.iov_base = packet;
iov.iov_len = packetlen;
iov.iov_len = length;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
@@ -634,7 +772,6 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
#ifdef HAVE_IN_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) {
struct cmsghdr *cmsg;
struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
@@ -652,7 +789,6 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
#ifdef HAVE_IN6_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET6) {
struct cmsghdr *cmsg;
struct in6_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
@@ -669,31 +805,27 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
}
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
if (process_tx)
cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd);
#endif
msg.msg_controllen = cmsglen;
/* This is apparently required on some systems */
if (!cmsglen)
msg.msg_control = NULL;
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
DEBUG_LOG("Could not send to %s:%d from %s fd %d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
strerror(errno));
return 0;
}
DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", packetlen,
DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length,
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
return 1;
}
/* ================================================== */
/* Send a packet to a given address */
int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
return send_packet((void *) packet, length, remote_addr, local_addr);
}

View File

@@ -54,6 +54,7 @@ extern void NIO_CloseServerSocket(int sock_fd);
extern int NIO_IsServerSocket(int sock_fd);
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx);
#endif /* GOT_NTP_IO_H */

665
ntp_io_linux.c Normal file
View File

@@ -0,0 +1,665 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Functions for NTP I/O specific to Linux
*/
#include "config.h"
#include "sysincl.h"
#include <ifaddrs.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include "array.h"
#include "conf.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
#include "ntp_sources.h"
#include "sched.h"
#include "sys_linux.h"
#include "util.h"
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
struct Interface {
char name[IF_NAMESIZE];
int if_index;
int phc_fd;
int phc_mode;
int phc_nocrossts;
/* Link speed in mbit/s */
int link_speed;
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
/* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
#define MAX_TS_DELAY 1.0
/* Array of Interfaces */
static ARR_Instance interfaces;
/* RX/TX and TX-specific timestamping socket options */
static int ts_flags;
static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* ================================================== */
static int
add_interface(CNF_HwTsInterface *conf_iface)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_fd, req_hwts_flags;
unsigned int i;
struct Interface *iface;
/* Check if the interface was not already added */
for (i = 0; i < ARR_GetSize(interfaces); i++) {
if (!strcmp(conf_iface->name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
return 1;
}
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
memset(&req, 0, sizeof (req));
memset(&ts_info, 0, sizeof (ts_info));
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
close(sock_fd);
return 0;
}
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
close(sock_fd);
return 0;
}
if_index = req.ifr_ifindex;
ts_info.cmd = ETHTOOL_GET_TS_INFO;
req.ifr_data = (char *)&ts_info;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return 0;
}
req_hwts_flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
close(sock_fd);
return 0;
}
ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON;
switch (conf_iface->rxfilter) {
case CNF_HWTS_RXFILTER_NONE:
ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
break;
case CNF_HWTS_RXFILTER_NTP:
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_NTP_ALL)) {
ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break;
}
#endif
/* Fall through */
default:
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
close(sock_fd);
return 0;
}
close(sock_fd);
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
return 0;
iface = ARR_GetNewElement(interfaces);
snprintf(iface->name, sizeof (iface->name), "%s", conf_iface->name);
iface->if_index = if_index;
iface->phc_fd = phc_fd;
iface->phc_mode = 0;
iface->phc_nocrossts = conf_iface->nocrossts;
/* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
iface->link_speed = 1000;
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
iface->clock = HCL_CreateInstance(UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
LOG(LOGS_INFO, "Enabled HW timestamping on %s", iface->name);
return 1;
}
/* ================================================== */
static int
add_all_interfaces(CNF_HwTsInterface *conf_iface_all)
{
CNF_HwTsInterface conf_iface;
struct ifaddrs *ifaddr, *ifa;
int r;
conf_iface = *conf_iface_all;
if (getifaddrs(&ifaddr)) {
DEBUG_LOG("getifaddrs() failed : %s", strerror(errno));
return 0;
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
conf_iface.name = ifa->ifa_name;
if (add_interface(&conf_iface))
r = 1;
}
freeifaddrs(ifaddr);
/* Return success if at least one interface was added */
return r;
}
/* ================================================== */
static void
update_interface_speed(struct Interface *iface)
{
struct ethtool_cmd cmd;
struct ifreq req;
int sock_fd;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return;
memset(&req, 0, sizeof (req));
memset(&cmd, 0, sizeof (cmd));
snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface->name);
cmd.cmd = ETHTOOL_GSET;
req.ifr_data = (char *)&cmd;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return;
}
close(sock_fd);
iface->link_speed = ethtool_cmd_speed(&cmd);
}
/* ================================================== */
#if defined(HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO) || defined(HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW)
static int
check_timestamping_option(int option)
{
int sock_fd;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
DEBUG_LOG("Could not enable timestamping option %x", option);
close(sock_fd);
return 0;
}
close(sock_fd);
return 1;
}
#endif
/* ================================================== */
void
NIO_Linux_Initialise(void)
{
CNF_HwTsInterface *conf_iface;
unsigned int i;
int hwts;
interfaces = ARR_CreateInstance(sizeof (struct Interface));
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
for (i = hwts = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (!strcmp("*", conf_iface->name))
continue;
if (!add_interface(conf_iface))
LOG_FATAL("Could not enable HW timestamping on %s", conf_iface->name);
hwts = 1;
}
for (i = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (strcmp("*", conf_iface->name))
continue;
if (add_all_interfaces(conf_iface))
hwts = 1;
break;
}
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
if (hwts) {
ts_flags |= SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ts_tx_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_PKTINFO))
ts_flags |= SOF_TIMESTAMPING_OPT_PKTINFO;
#endif
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_TX_SWHW))
ts_flags |= SOF_TIMESTAMPING_OPT_TX_SWHW;
#endif
}
/* Enable IP_PKTINFO in messages looped back to the error queue */
ts_flags |= SOF_TIMESTAMPING_OPT_CMSG;
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
}
/* ================================================== */
void
NIO_Linux_Finalise(void)
{
struct Interface *iface;
unsigned int i;
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
ARR_DestroyInstance(interfaces);
}
/* ================================================== */
int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
{
int val, flags;
if (!ts_flags)
return 0;
/* Enable SCM_TIMESTAMPING control messages and the socket's error queue in
order to receive our transmitted packets with more accurate timestamps */
val = 1;
flags = ts_flags;
if (client_only || permanent_ts_options)
flags |= ts_tx_flags;
if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
ts_flags = 0;
return 0;
}
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
ts_flags = 0;
return 0;
}
*events |= SCH_FILE_EXCEPTION;
return 1;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
struct Interface *iface;
unsigned int i;
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
if (iface->if_index != if_index)
continue;
return iface;
}
return NULL;
}
/* ================================================== */
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double rx_correction, ts_delay, phc_err, local_err;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
update_interface_speed(iface);
}
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
timestamps. If we don't know the length of the packet at layer 2, we
make an assumption that UDP data start at the same position as in the
last transmitted packet which had a HW TX timestamp. */
if (rx_ntp_length && iface->link_speed) {
if (!l2_length)
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
}
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &local_err))
return;
if (!rx_ntp_length && iface->tx_comp)
UTI_AddDoubleToTimespec(&ts, iface->tx_comp, &ts);
else if (rx_ntp_length && iface->rx_comp)
UTI_AddDoubleToTimespec(&ts, -iface->rx_comp, &ts);
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_HARDWARE;
}
/* ================================================== */
/* Extract UDP data from a layer 2 message. Supported is Ethernet
with optional VLAN tags. */
static int
extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
{
unsigned char *msg_start = msg;
union sockaddr_in46 addr;
remote_addr->ip_addr.family = IPADDR_UNSPEC;
remote_addr->port = 0;
/* Skip MACs */
if (len < 12)
return 0;
len -= 12, msg += 12;
/* Skip VLAN tag(s) if present */
while (len >= 4 && msg[0] == 0x81 && msg[1] == 0x00)
len -= 4, msg += 4;
/* Skip IPv4 or IPv6 ethertype */
if (len < 2 || !((msg[0] == 0x08 && msg[1] == 0x00) ||
(msg[0] == 0x86 && msg[1] == 0xdd)))
return 0;
len -= 2, msg += 2;
/* Parse destination address and port from IPv4/IPv6 and UDP headers */
if (len >= 20 && msg[0] >> 4 == 4) {
int ihl = (msg[0] & 0xf) * 4;
if (len < ihl + 8 || msg[9] != 17)
return 0;
memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t));
addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2);
addr.in4.sin_family = AF_INET;
len -= ihl + 8, msg += ihl + 8;
#ifdef FEAT_IPV6
} else if (len >= 48 && msg[0] >> 4 == 6) {
/* IPv6 extension headers are not supported */
if (msg[6] != 17)
return 0;
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
addr.in6.sin6_port = *(uint16_t *)(msg + 40 + 2);
addr.in6.sin6_family = AF_INET6;
len -= 48, msg += 48;
#endif
} else {
return 0;
}
UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port);
/* Move the message to fix alignment of its fields */
if (len > 0)
memmove(msg_start, msg, len);
return len;
}
/* ================================================== */
int
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
{
struct Interface *iface;
struct cmsghdr *cmsg;
int is_tx, ts_if_index, l2_length;
is_tx = hdr->msg_flags & MSG_ERRQUEUE;
iface = NULL;
ts_if_index = local_addr->if_index;
l2_length = 0;
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) {
struct scm_ts_pktinfo ts_pktinfo;
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
ts_if_index = ts_pktinfo.if_index;
l2_length = ts_pktinfo.pkt_length;
DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length);
}
#endif
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(ts_if_index);
if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
remote_addr->ip_addr.family, l2_length);
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
(!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
}
}
if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
(cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
struct sock_extended_err err;
memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
DEBUG_LOG("Unknown extended error");
/* Drop the message */
return 1;
}
}
}
/* Return the message if it's not received from the error queue */
if (!is_tx)
return 0;
/* The data from the error queue includes all layers up to UDP. We have to
extract the UDP data and also the destination address with port as there
currently doesn't seem to be a better way to get them both. */
l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
local_addr->sock_fd, local_addr->if_index, local_ts->source);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && length) {
if (remote_addr->ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - length;
else if (remote_addr->ip_addr.family == IPADDR_INET6)
iface->l2_udp6_ntp_start = l2_length - length;
}
/* Drop the message if it has no timestamp or its processing failed */
if (local_ts->source == NTP_TS_DAEMON) {
DEBUG_LOG("Missing TX timestamp");
return 1;
}
if (length < NTP_NORMAL_PACKET_LENGTH)
return 1;
NSR_ProcessTx(remote_addr, local_addr, local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
return 1;
}
/* ================================================== */
int
NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen;
/* Add control message that will enable TX timestamping for this message.
Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
control messages. */
cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen);
memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMPING;
cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags));
memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags));
return cmsglen;
}

36
ntp_io_linux.h Normal file
View File

@@ -0,0 +1,36 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
This is the header file for the Linux-specific NTP socket I/O bits.
*/
extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);

379
ntp_signd.c Normal file
View File

@@ -0,0 +1,379 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Support for MS-SNTP authentication in Samba (ntp_signd)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "conf.h"
#include "logging.h"
#include "ntp_io.h"
#include "ntp_signd.h"
#include "sched.h"
#include "util.h"
/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
#define SIGND_VERSION 0
typedef enum {
SIGN_TO_CLIENT = 0,
ASK_SERVER_TO_SIGN = 1,
CHECK_SERVER_SIGNATURE = 2,
SIGNING_SUCCESS = 3,
SIGNING_FAILURE = 4,
} SigndOp;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint16_t packet_id;
uint16_t _pad;
uint32_t key_id;
NTP_Packet packet_to_sign;
} SigndRequest;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint32_t packet_id;
NTP_Packet signed_packet;
} SigndResponse;
typedef struct {
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
int sent;
int received;
int request_length;
struct timespec request_ts;
SigndRequest request;
SigndResponse response;
} SignInstance;
/* As the communication with ntp_signd is asynchronous, incoming packets are
saved in a queue in order to avoid loss when they come in bursts */
#define MAX_QUEUE_LENGTH 16U
#define NEXT_QUEUE_INDEX(index) (((index) + 1) % MAX_QUEUE_LENGTH)
#define IS_QUEUE_EMPTY() (queue_head == queue_tail)
/* Fixed-size array of SignInstance */
static ARR_Instance queue;
static unsigned int queue_head;
static unsigned int queue_tail;
#define INVALID_SOCK_FD -1
/* Unix domain socket connected to ntp_signd */
static int sock_fd;
#define MIN_AUTH_DELAY 1.0e-5
#define MAX_AUTH_DELAY 1.0e-2
/* Average time needed for signing one packet. This is used to adjust the
transmit timestamp in NTP packets. The timestamp won't be very accurate as
the delay is variable, but it should be good enough for MS-SNTP clients. */
static double auth_delay;
/* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled;
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static void
close_socket(void)
{
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
sock_fd = INVALID_SOCK_FD;
/* Empty the queue */
queue_head = queue_tail = 0;
}
/* ================================================== */
static int
open_socket(void)
{
struct sockaddr_un s;
if (sock_fd >= 0)
return 1;
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
return 0;
}
UTI_FdSetCloexec(sock_fd);
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
DEBUG_LOG("signd socket path too long");
close_socket();
return 0;
}
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
close_socket();
return 0;
}
DEBUG_LOG("Connected to signd");
return 1;
}
/* ================================================== */
static void
process_response(SignInstance *inst)
{
struct timespec ts;
double delay;
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
DEBUG_LOG("Invalid response ID");
return;
}
if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
DEBUG_LOG("Signing failed");
return;
}
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG("Invalid NTP socket");
return;
}
SCH_GetLastEventTime(NULL, NULL, &ts);
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
DEBUG_LOG("Signing succeeded (delay %f)", delay);
/* Send the signed NTP packet */
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
ntohl(inst->response.length) + sizeof (inst->response.length) -
offsetof(SigndResponse, signed_packet), 0);
/* Update exponential moving average of the authentication delay */
delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
auth_delay += 0.1 * (delay - auth_delay);
}
/* ================================================== */
static void
read_write_socket(int sock_fd, int event, void *anything)
{
SignInstance *inst;
uint32_t response_length;
int s;
inst = ARR_GetElement(queue, queue_head);
if (event == SCH_FILE_OUTPUT) {
assert(!IS_QUEUE_EMPTY());
assert(inst->sent < inst->request_length);
if (!inst->sent)
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
s = send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0);
if (s < 0) {
DEBUG_LOG("signd socket error: %s", strerror(errno));
close_socket();
return;
}
DEBUG_LOG("Sent %d bytes to signd", s);
inst->sent += s;
/* Try again later if the request is not complete yet */
if (inst->sent < inst->request_length)
return;
/* Disable output and wait for a response */
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
}
if (event == SCH_FILE_INPUT) {
if (IS_QUEUE_EMPTY()) {
DEBUG_LOG("Unexpected signd response");
close_socket();
return;
}
assert(inst->received < sizeof (inst->response));
s = recv(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0);
if (s <= 0) {
if (s < 0)
DEBUG_LOG("signd socket error: %s", strerror(errno));
else
DEBUG_LOG("signd socket closed");
close_socket();
return;
}
DEBUG_LOG("Received %d bytes from signd", s);
inst->received += s;
if (inst->received < sizeof (inst->response.length))
return;
response_length = ntohl(inst->response.length) + sizeof (inst->response.length);
if (response_length < offsetof(SigndResponse, signed_packet) ||
response_length > sizeof (SigndResponse)) {
DEBUG_LOG("Invalid response length");
close_socket();
return;
}
/* Wait for more data if not complete yet */
if (inst->received < response_length)
return;
process_response(inst);
/* Move the head and enable output for the next packet */
queue_head = NEXT_QUEUE_INDEX(queue_head);
if (!IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
}
}
/* ================================================== */
void
NSD_Initialise()
{
sock_fd = INVALID_SOCK_FD;
auth_delay = MIN_AUTH_DELAY;
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
if (!enabled)
return;
queue = ARR_CreateInstance(sizeof (SignInstance));
ARR_SetSize(queue, MAX_QUEUE_LENGTH);
queue_head = queue_tail = 0;
LOG(LOGS_INFO, "MS-SNTP authentication enabled");
}
/* ================================================== */
void
NSD_Finalise()
{
if (!enabled)
return;
if (sock_fd != INVALID_SOCK_FD)
close_socket();
ARR_DestroyInstance(queue);
}
/* ================================================== */
extern int NSD_GetAuthDelay(uint32_t key_id)
{
return 1.0e9 * auth_delay;
}
/* ================================================== */
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
SignInstance *inst;
if (!enabled) {
DEBUG_LOG("signd disabled");
return 0;
}
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
DEBUG_LOG("signd queue full");
return 0;
}
if (length != NTP_NORMAL_PACKET_LENGTH) {
DEBUG_LOG("Invalid packet length");
return 0;
}
if (!open_socket())
return 0;
inst = ARR_GetElement(queue, queue_tail);
inst->remote_addr = *remote_addr;
inst->local_addr = *local_addr;
inst->sent = 0;
inst->received = 0;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
/* The length field doesn't include itself */
inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
inst->request.version = htonl(SIGND_VERSION);
inst->request.op = htonl(SIGN_TO_CLIENT);
inst->request.packet_id = htons(queue_tail);
inst->request._pad = 0;
inst->request.key_id = htonl(key_id);
memcpy(&inst->request.packet_to_sign, packet, length);
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
return 1;
}

44
ntp_signd.h Normal file
View File

@@ -0,0 +1,44 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header for MS-SNTP authentication via Samba (ntp_signd) */
#ifndef GOT_NTP_SIGND_H
#define GOT_NTP_SIGND_H
#include "addressing.h"
#include "ntp.h"
/* Initialisation function */
extern void NSD_Initialise(void);
/* Finalisation function */
extern void NSD_Finalise(void);
/* Function to get an estimate of delay due to signing */
extern int NSD_GetAuthDelay(uint32_t key_id);
/* Function to sign an NTP packet and send it */
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
#endif

View File

@@ -39,6 +39,7 @@
#include "local.h"
#include "memory.h"
#include "nameserv_async.h"
#include "privops.h"
#include "sched.h"
/* ================================================== */
@@ -117,8 +118,8 @@ static void rehash_records(void);
static void clean_source_record(SourceRecord *record);
static void
slew_sources(struct timeval *raw,
struct timeval *cooked,
slew_sources(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -354,7 +355,7 @@ replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
/* The hash table must be rebuilt for the new address */
rehash_records();
LOG(LOGS_INFO, LOGF_NtpSources, "Source %s replaced with %s",
LOG(LOGS_INFO, "Source %s replaced with %s",
UTI_IPToString(&old_addr->ip_addr),
UTI_IPToString(&new_addr->ip_addr));
@@ -377,7 +378,7 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
address.port = us->port;
DEBUG_LOG(LOGF_NtpSources, "(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
if (us->replacement) {
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
@@ -404,7 +405,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
assert(us == resolving_source);
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %d addrs", us->name, n_addrs);
DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs);
switch (status) {
case DNS_TryAgain:
@@ -413,7 +414,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
process_resolved_name(us, ip_addrs, n_addrs);
break;
case DNS_Failure:
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name);
LOG(LOGS_WARN, "Invalid host %s", us->name);
break;
default:
assert(0);
@@ -438,7 +439,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
if (next) {
/* Continue with the next source in the list */
DEBUG_LOG(LOGF_NtpSources, "resolving %s", next->name);
DEBUG_LOG("resolving %s", next->name);
DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
} else {
/* This was the last source in the list. If some sources couldn't
@@ -469,14 +470,14 @@ resolve_sources(void *arg)
assert(!resolving_source);
DNS_Reload();
PRV_ReloadDNS();
/* Start with the first source in the list, name_resolve_handler
will iterate over the rest */
us = unresolved_sources;
resolving_source = us;
DEBUG_LOG(LOGF_NtpSources, "resolving %s", us->name);
DEBUG_LOG("resolving %s", us->name);
DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
}
@@ -657,8 +658,7 @@ resolve_source_replacement(SourceRecord *record)
{
struct UnresolvedSource *us;
DEBUG_LOG(LOGF_NtpSources, "trying to replace %s",
UTI_IPToString(&record->remote_addr->ip_addr));
DEBUG_LOG("trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr));
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
@@ -680,8 +680,8 @@ resolve_source_replacement(SourceRecord *record)
void
NSR_HandleBadSource(IPAddr *address)
{
static struct timeval last_replacement;
struct timeval now;
static struct timespec last_replacement;
struct timespec now;
NTP_Remote_Address remote_addr;
SourceRecord *record;
int slot, found;
@@ -702,9 +702,9 @@ NSR_HandleBadSource(IPAddr *address)
/* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now);
UTI_DiffTimevalsToDouble(&diff, &now, &last_replacement);
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG(LOGF_NtpSources, "replacement postponed");
DEBUG_LOG("replacement postponed");
return;
}
last_replacement = now;
@@ -742,7 +742,7 @@ static void remove_tentative_pool_sources(int pool)
if (!record->remote_addr || record->pool != pool || !record->tentative)
continue;
DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s",
DEBUG_LOG("removing tentative source %s",
UTI_IPToString(&record->remote_addr->ip_addr));
clean_source_record(record);
@@ -776,7 +776,8 @@ NSR_GetLocalRefid(IPAddr *address)
/* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */
void
NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
SourceRecord *record;
struct SourcePool *pool;
@@ -788,19 +789,18 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
if (found == 2) { /* Must match IP address AND port number */
record = get_record(slot);
if (!NCR_ProcessKnown(message, now, now_err, record->data, local_addr, length))
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
return;
if (record->tentative) {
/* This was the first valid reply from the source */
/* This was the first good reply from the source */
record->tentative = 0;
if (record->pool != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources++;
DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources",
record->name, pool->sources);
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources);
/* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */
@@ -809,15 +809,34 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
}
}
} else {
NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length);
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
}
}
/* ================================================== */
void
NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
{
SourceRecord *record;
int slot, found;
find_slot(remote_addr, &slot, &found);
if (found == 2) { /* Must match IP address AND port number */
record = get_record(slot);
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
} else {
NCR_ProcessTxUnknown(remote_addr, local_addr, tx_ts, message, length);
}
}
/* ================================================== */
static void
slew_sources(struct timeval *raw,
struct timeval *cooked,
slew_sources(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -831,6 +850,7 @@ slew_sources(struct timeval *raw,
if (record->remote_addr) {
if (change_type == LCL_ChangeUnknownStep) {
NCR_ResetInstance(record->data);
NCR_ResetPoll(record->data);
} else {
NCR_SlewTimes(record->data, cooked, dfreq, doffset);
}
@@ -1083,7 +1103,7 @@ NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
identify the source record. */
void
NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
NTP_Remote_Address rem_addr;
int slot, found;
@@ -1099,6 +1119,26 @@ NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
}
}
/* ================================================== */
/* The ip address is assumed to be completed on input, that is how we
identify the source record. */
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
NTP_Remote_Address rem_addr;
int slot, found;
rem_addr.ip_addr = report->remote_addr;
rem_addr.port = 0;
find_slot(&rem_addr, &slot, &found);
if (!found)
return 0;
NCR_GetNTPReport(get_record(slot)->data, report);
return 1;
}
/* ================================================== */
void

View File

@@ -87,7 +87,13 @@ extern void NSR_RefreshAddresses(void);
extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called by ntp_io when a packet was sent to the network and
an accurate transmit timestamp was captured */
extern void NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Initialisation function */
extern void NSR_Initialise(void);
@@ -121,7 +127,9 @@ extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
extern void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now);
extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report);

View File

@@ -82,8 +82,8 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENYALL */
REQ_LENGTH_ENTRY(ac_check, null), /* ACCHECK */
REQ_LENGTH_ENTRY(ac_check, null), /* CMDACCHECK */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER */
{ 0, 0 }, /* ADD_SERVER */
{ 0, 0 }, /* ADD_PEER */
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */
@@ -113,6 +113,9 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER2 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER2 */
};
static const uint16_t reply_lengths[] = {
@@ -120,7 +123,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(null), /* NULL */
RPY_LENGTH_ENTRY(n_sources), /* N_SOURCES */
RPY_LENGTH_ENTRY(source_data), /* SOURCE_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP */
0, /* MANUAL_TIMESTAMP */
RPY_LENGTH_ENTRY(tracking), /* TRACKING */
RPY_LENGTH_ENTRY(sourcestats), /* SOURCESTATS */
RPY_LENGTH_ENTRY(rtc), /* RTC */
@@ -132,6 +135,8 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
};
/* ================================================== */

View File

@@ -40,6 +40,7 @@
#define OP_SETTIME 1026
#define OP_BINDSOCKET 1027
#define OP_NAME2IPADDRESS 1028
#define OP_RELOADDNS 1029
#define OP_QUIT 1099
union sockaddr_in46 {
@@ -293,8 +294,6 @@ do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
/* make sure the string is terminated */
req->name[sizeof (req->name) - 1] = '\0';
DNS_Reload();
res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses,
DNS_MAX_ADDRESSES);
}
@@ -302,6 +301,19 @@ do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
/* ======================================================================= */
/* HELPER - perform DNS_Reload() */
#ifdef PRIVOPS_RELOADDNS
static void
do_reload_dns(PrvResponse *res)
{
DNS_Reload();
res->rc = 0;
}
#endif
/* ======================================================================= */
/* HELPER - main loop - action requests from the daemon */
static void
@@ -343,6 +355,11 @@ helper_main(int fd)
case OP_NAME2IPADDRESS:
do_name_to_ipaddress(&req.data.name_to_ipaddress, &res);
break;
#endif
#ifdef PRIVOPS_RELOADDNS
case OP_RELOADDNS:
do_reload_dns(&res);
break;
#endif
case OP_QUIT:
quit = 1;
@@ -371,14 +388,14 @@ receive_response(PrvResponse *res)
resp_len = recv(helper_fd, res, sizeof (*res), 0);
if (resp_len < 0)
LOG_FATAL(LOGF_PrivOps, "Could not read from helper : %s", strerror(errno));
LOG_FATAL("Could not read from helper : %s", strerror(errno));
if (resp_len != sizeof (*res))
LOG_FATAL(LOGF_PrivOps, "Invalid helper response");
LOG_FATAL("Invalid helper response");
if (res->fatal_error)
LOG_FATAL(LOGF_PrivOps, "Error in helper : %s", res->data.fatal_msg.msg);
LOG_FATAL("Error in helper : %s", res->data.fatal_msg.msg);
DEBUG_LOG(LOGF_PrivOps, "Received response rc=%d", res->rc);
DEBUG_LOG("Received response rc=%d", res->rc);
/* if operation failed in the helper, set errno so daemon can print log message */
if (res->res_errno)
@@ -429,10 +446,10 @@ send_request(PrvRequest *req)
if (sendmsg(helper_fd, &msg, 0) < 0) {
/* don't try to send another request from exit() */
helper_fd = -1;
LOG_FATAL(LOGF_PrivOps, "Could not send to helper : %s", strerror(errno));
LOG_FATAL("Could not send to helper : %s", strerror(errno));
}
DEBUG_LOG(LOGF_PrivOps, "Sent request op=%d", req->op);
DEBUG_LOG("Sent request op=%d", req->op);
}
/* ======================================================================= */
@@ -598,7 +615,7 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
req.op = OP_NAME2IPADDRESS;
if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
"%s", name) >= sizeof (req.data.name_to_ipaddress.name)) {
DEBUG_LOG(LOGF_PrivOps, "Name too long");
DEBUG_LOG("Name too long");
return DNS_Failure;
}
@@ -613,6 +630,30 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
/* ======================================================================= */
/* DAEMON - request res_init() */
#ifdef PRIVOPS_RELOADDNS
void
PRV_ReloadDNS(void)
{
PrvRequest req;
PrvResponse res;
if (!have_helper()) {
DNS_Reload();
return;
}
memset(&req, 0, sizeof (req));
req.op = OP_RELOADDNS;
submit_request(&req, &res);
assert(!res.rc);
}
#endif
/* ======================================================================= */
void
PRV_Initialise(void)
{
@@ -631,21 +672,21 @@ PRV_StartHelper(void)
int fd, sock_pair[2];
if (have_helper())
LOG_FATAL(LOGF_PrivOps, "Helper already running");
LOG_FATAL("Helper already running");
if (
#ifdef SOCK_SEQPACKET
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) &&
#endif
socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair))
LOG_FATAL(LOGF_PrivOps, "socketpair() failed : %s", strerror(errno));
LOG_FATAL("socketpair() failed : %s", strerror(errno));
UTI_FdSetCloexec(sock_pair[0]);
UTI_FdSetCloexec(sock_pair[1]);
pid = fork();
if (pid < 0)
LOG_FATAL(LOGF_PrivOps, "fork() failed : %s", strerror(errno));
LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid == 0) {
/* child process */

View File

@@ -58,6 +58,12 @@ int PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs);
#define PRV_Name2IPAddress DNS_Name2IPAddress
#endif
#ifdef PRIVOPS_RELOADDNS
void PRV_ReloadDNS(void);
#else
#define PRV_ReloadDNS DNS_Reload
#endif
#ifdef PRIVOPS_HELPER
void PRV_Initialise(void);
void PRV_StartHelper(void);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -48,7 +48,7 @@ extern RefclockDriver RCL_PHC_driver;
struct FilterSample {
double offset;
double dispersion;
struct timeval sample_time;
struct timespec sample_time;
};
struct MedianFilter {
@@ -75,14 +75,17 @@ struct RCL_Instance_Record {
int driver_polled;
int poll;
int leap_status;
int pps_forced;
int pps_rate;
int pps_active;
int max_lock_age;
struct MedianFilter filter;
uint32_t ref_id;
uint32_t lock_ref;
double offset;
double delay;
double precision;
double pulse_width;
SCH_TimeoutID timeout_id;
SRC_Instance source;
};
@@ -92,24 +95,24 @@ static ARR_Instance refclocks;
static LOG_FileID logfileid;
static int valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked);
static int pps_stratum(RCL_Instance instance, struct timeval *tv);
static int valid_sample_time(RCL_Instance instance, struct timespec *sample_time);
static int pps_stratum(RCL_Instance instance, struct timespec *ts);
static void poll_timeout(void *arg);
static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything);
static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
static void filter_fini(struct MedianFilter *filter);
static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion);
static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion);
static int filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
static int filter_get_samples(struct MedianFilter *filter);
static int filter_select_samples(struct MedianFilter *filter);
static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
static int filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset);
static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
static RCL_Instance
@@ -163,7 +166,6 @@ RCL_Finalise(void)
int
RCL_AddRefclock(RefclockParameters *params)
{
int pps_source = 0;
RCL_Instance inst;
inst = MallocNew(struct RCL_Instance_Record);
@@ -171,25 +173,19 @@ RCL_AddRefclock(RefclockParameters *params)
if (strcmp(params->driver_name, "SHM") == 0) {
inst->driver = &RCL_SHM_driver;
inst->precision = 1e-6;
} else if (strcmp(params->driver_name, "SOCK") == 0) {
inst->driver = &RCL_SOCK_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PPS") == 0) {
inst->driver = &RCL_PPS_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PHC") == 0) {
inst->driver = &RCL_PHC_driver;
inst->precision = 1e-9;
} else {
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
LOG_FATAL("unknown refclock driver %s", params->driver_name);
return 0;
}
if (!inst->driver->init && !inst->driver->poll) {
LOG_FATAL(LOGF_Refclock, "refclock driver %s is not compiled in", params->driver_name);
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
return 0;
}
@@ -200,13 +196,16 @@ RCL_AddRefclock(RefclockParameters *params)
inst->poll = params->poll;
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age;
inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset;
inst->delay = params->delay;
if (params->precision > 0.0)
inst->precision = params->precision;
inst->precision = LCL_GetSysPrecisionAsQuantum();
inst->precision = MAX(inst->precision, params->precision);
inst->pulse_width = params->pulse_width;
inst->timeout_id = -1;
inst->source = NULL;
@@ -219,12 +218,8 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_parameter[i] = '\0';
}
if (pps_source) {
if (inst->pps_rate < 1)
inst->pps_rate = 1;
} else {
inst->pps_rate = 0;
}
if (inst->pps_rate < 1)
inst->pps_rate = 1;
if (params->ref_id)
inst->ref_id = params->ref_id;
@@ -249,7 +244,7 @@ RCL_AddRefclock(RefclockParameters *params)
max_samples = 1 << (inst->poll - inst->driver_poll);
if (max_samples < params->filter_length) {
if (max_samples < 4) {
LOG(LOGS_WARN, LOGF_Refclock, "Setting filter length for %s to %d",
LOG(LOGS_WARN, "Setting filter length for %s to %d",
UTI_RefidToString(inst->ref_id), max_samples);
}
params->filter_length = max_samples;
@@ -258,7 +253,7 @@ RCL_AddRefclock(RefclockParameters *params)
if (inst->driver->init)
if (!inst->driver->init(inst)) {
LOG_FATAL(LOGF_Refclock, "refclock %s initialisation failed", params->driver_name);
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
return 0;
}
@@ -267,7 +262,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples);
DEBUG_LOG(LOGF_Refclock, "refclock %s refid=%s poll=%d dpoll=%d filter=%d",
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id),
inst->poll, inst->driver_poll, params->filter_length);
@@ -300,7 +295,7 @@ RCL_StartRefclocks(void)
}
void
RCL_ReportSource(RPT_SourceReport *report, struct timeval *now)
RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
unsigned int i;
uint32_t ref_id;
@@ -362,18 +357,21 @@ RCL_GetDriverOption(RCL_Instance instance, char *name)
}
int
RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap)
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
double correction, dispersion;
struct timeval cooked_time;
struct timespec cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time, -offset);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time, &cooked_time))
!valid_sample_time(instance, &cooked_time))
return 0;
switch (leap) {
@@ -383,7 +381,7 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
instance->leap_status = leap;
break;
default:
DEBUG_LOG(LOGF_Refclock, "refclock sample ignored bad leap %d", leap);
DEBUG_LOG("refclock sample ignored bad leap %d", leap);
return 0;
}
@@ -400,26 +398,59 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
}
int
RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
{
double correction, dispersion, offset;
struct timeval cooked_time;
double correction, dispersion;
struct timespec cooked_time;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
second += correction;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
return 0;
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
}
static int
check_pulse_edge(RCL_Instance instance, double offset, double distance)
{
double max_error;
if (instance->pulse_width <= 0.0)
return 1;
max_error = 1.0 / instance->pps_rate - instance->pulse_width;
max_error = MIN(instance->pulse_width, max_error);
max_error *= 0.5;
if (fabs(offset) > max_error || distance > max_error) {
DEBUG_LOG("refclock pulse ignored offset=%.9f distance=%.9f max_error=%.9f",
offset, distance, max_error);
return 0;
}
return 1;
}
int
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction)
{
double offset;
int rate;
NTP_Leap leap;
leap = LEAP_Normal;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
!valid_sample_time(instance, pulse_time, &cooked_time))
if (!UTI_IsTimeOffsetSane(cooked_time, second) ||
!valid_sample_time(instance, cooked_time))
return 0;
leap = LEAP_Normal;
dispersion += instance->precision;
rate = instance->pps_rate;
assert(rate > 0);
offset = -second - correction + instance->offset;
offset = -second + instance->offset;
/* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
offset -= (long)(offset * rate) / (double)rate;
@@ -430,22 +461,22 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
if (instance->lock_ref != -1) {
RCL_Instance lock_refclock;
struct timeval ref_sample_time;
struct timespec ref_sample_time;
double sample_diff, ref_offset, ref_dispersion, shift;
lock_refclock = get_refclock(instance->lock_ref);
if (!filter_get_last_sample(&lock_refclock->filter,
&ref_sample_time, &ref_offset, &ref_dispersion)) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample");
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= 2.0 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff);
return 0;
}
@@ -459,17 +490,20 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
offset += shift;
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_offset - offset, ref_dispersion, dispersion);
return 0;
}
if (!check_pulse_edge(instance, ref_offset - offset, 0.0))
return 0;
leap = lock_refclock->leap_status;
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff);
DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
offset, ref_offset - offset, sample_diff);
} else {
struct timeval ref_time;
struct timespec ref_time;
int is_synchronised, stratum;
double root_delay, root_dispersion, distance;
uint32_t ref_id;
@@ -477,24 +511,28 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
/* Ignore the pulse if we are not well synchronized and the local
reference is not active */
REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum,
REF_GetReferenceParams(cooked_time, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
distance = fabs(root_delay) / 2 + root_dispersion;
if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
second, leap != LEAP_Unsynchronised, distance);
DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
offset, leap != LEAP_Unsynchronised, distance);
/* Drop also all stored samples */
filter_reset(&instance->filter);
return 0;
}
if (!check_pulse_edge(instance, offset, distance))
return 0;
}
filter_add_sample(&instance->filter, &cooked_time, offset, dispersion);
filter_add_sample(&instance->filter, cooked_time, offset, dispersion);
instance->leap_status = leap;
instance->pps_active = 1;
log_sample(instance, &cooked_time, 0, 1, offset + correction - instance->offset, offset, dispersion);
log_sample(instance, cooked_time, 0, 1, offset + raw_correction - instance->offset,
offset, dispersion);
/* for logging purposes */
if (!instance->driver->poll)
@@ -503,23 +541,35 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
return 1;
}
static int
valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked)
double
RCL_GetPrecision(RCL_Instance instance)
{
struct timeval raw_time, last_sample_time;
return instance->precision;
}
int
RCL_GetDriverPoll(RCL_Instance instance)
{
return instance->driver_poll;
}
static int
valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
{
struct timespec now, last_sample_time;
double diff, last_offset, last_dispersion;
LCL_ReadRawTime(&raw_time);
UTI_DiffTimevalsToDouble(&diff, &raw_time, raw);
LCL_ReadCookedTime(&now, NULL);
diff = UTI_DiffTimespecsToDouble(&now, sample_time);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
(filter_get_samples(&instance->filter) > 0 &&
filter_get_last_sample(&instance->filter, &last_sample_time,
&last_offset, &last_dispersion) &&
UTI_CompareTimevals(&last_sample_time, cooked) >= 0)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f raw=%s cooked=%s",
UTI_RefidToString(instance->ref_id), diff,
UTI_TimevalToString(raw), UTI_TimevalToString(cooked));
UTI_CompareTimespecs(&last_sample_time, sample_time) >= 0)) {
DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
UTI_RefidToString(instance->ref_id),
UTI_TimespecToString(sample_time), diff);
return 0;
}
@@ -527,9 +577,9 @@ valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *co
}
static int
pps_stratum(RCL_Instance instance, struct timeval *tv)
pps_stratum(RCL_Instance instance, struct timespec *ts)
{
struct timeval ref_time;
struct timespec ref_time;
int is_synchronised, stratum;
unsigned int i;
double root_delay, root_dispersion;
@@ -537,7 +587,7 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
uint32_t ref_id;
RCL_Instance refclock;
REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum,
REF_GetReferenceParams(ts, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
/* Don't change our stratum if the local reference is active
@@ -574,7 +624,7 @@ poll_timeout(void *arg)
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion;
struct timeval sample_time;
struct timespec sample_time;
int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
@@ -602,7 +652,7 @@ poll_timeout(void *arg)
}
static void
slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
unsigned int i;
@@ -625,7 +675,7 @@ add_dispersion(double dispersion, void *anything)
}
static void
log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
{
char sync_stats[4] = {'N', '+', '-', '?'};
@@ -635,7 +685,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
if (!filtered) {
LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec),
(int)sample_time->tv_usec,
(int)sample_time->tv_nsec / 1000,
UTI_RefidToString(instance->ref_id),
instance->driver_polled,
sync_stats[instance->leap_status],
@@ -646,7 +696,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
} else {
LOG_FileWrite(logfileid, "%s.%06d %-5s - %1c - - %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec),
(int)sample_time->tv_usec,
(int)sample_time->tv_nsec / 1000,
UTI_RefidToString(instance->ref_id),
sync_stats[instance->leap_status],
cooked_offset,
@@ -699,7 +749,7 @@ filter_get_avg_sample_dispersion(struct MedianFilter *filter)
}
static void
filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion)
filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
{
filter->index++;
filter->index %= filter->length;
@@ -711,12 +761,12 @@ filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
filter->samples[filter->index].offset = offset;
filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimevalToString(sample_time), offset, dispersion);
DEBUG_LOG("filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
}
static int
filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
if (filter->last < 0)
return 0;
@@ -835,7 +885,7 @@ filter_select_samples(struct MedianFilter *filter)
}
static int
filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
{
struct FilterSample *s, *ls;
int i, n, dof;
@@ -852,7 +902,7 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
for (i = 0; i < n; i++) {
s = &filter->samples[filter->selected[i]];
UTI_DiffTimevalsToDouble(&filter->x_data[i], &s->sample_time, &ls->sample_time);
filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
filter->y_data[i] = s->offset;
filter->w_data[i] = s->dispersion;
}
@@ -901,7 +951,7 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
/* drop the sample if variance is larger than allowed maximum */
if (filter->max_var > 0.0 && var > filter->max_var) {
DEBUG_LOG(LOGF_Refclock, "filter dispersion too large disp=%.9f max=%.9f",
DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
sqrt(var), sqrt(filter->max_var));
return 0;
}
@@ -927,7 +977,7 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
if (d < e)
d = e;
UTI_AddDoubleToTimeval(&ls->sample_time, x, sample_time);
UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
*offset = y;
*dispersion = d;
@@ -937,15 +987,26 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
}
static void
filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset)
filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
{
int i;
int i, first, last;
double delta_time;
struct timeval *sample;
struct timespec *sample;
for (i = 0; i < filter->used; i++) {
if (filter->last < 0)
return;
/* always slew the last sample as it may be needed by PPS refclocks */
if (filter->used > 0) {
first = 0;
last = filter->used - 1;
} else {
first = last = filter->last;
}
for (i = first; i <= last; i++) {
sample = &filter->samples[i].sample_time;
UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
filter->samples[i].offset -= delta_time;
}
}

View File

@@ -37,16 +37,19 @@ typedef struct {
int driver_poll;
int poll;
int filter_length;
int pps_forced;
int pps_rate;
int min_samples;
int max_samples;
int sel_options;
int max_lock_age;
uint32_t ref_id;
uint32_t lock_ref_id;
double offset;
double delay;
double precision;
double max_dispersion;
double pulse_width;
} RefclockParameters;
typedef struct RCL_Instance_Record *RCL_Instance;
@@ -61,14 +64,18 @@ extern void RCL_Initialise(void);
extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
/* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);
extern double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2013
* Copyright (C) Miroslav Lichvar 2013, 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,151 +33,134 @@
#include "sysincl.h"
#include <linux/ptp_clock.h>
#include "refclock.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
#include "sched.h"
#include "sys_linux.h"
/* From linux/include/linux/posix-timers.h */
#define CPUCLOCK_MAX 3
#define CLOCKFD CPUCLOCK_MAX
#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
#define NUM_READINGS 10
static int no_sys_offset_ioctl = 0;
struct phc_reading {
struct timespec sys_ts1;
struct timespec phc_ts;;
struct timespec sys_ts2;
struct phc_instance {
int fd;
int mode;
int nocrossts;
int extpps;
int pin;
int channel;
HCL_Instance clock;
};
static double diff_ts(struct timespec *ts1, struct timespec *ts2)
{
return (ts1->tv_sec - ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
}
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
{
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
struct ptp_sys_offset sys_off;
int i;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = n;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
LOG(LOGS_ERR, LOGF_Refclock, "ioctl(PTP_SYS_OFFSET) failed : %s", strerror(errno));
return 0;
}
for (i = 0; i < n; i++) {
readings[i].sys_ts1.tv_sec = sys_off.ts[i * 2].sec;
readings[i].sys_ts1.tv_nsec = sys_off.ts[i * 2].nsec;
readings[i].phc_ts.tv_sec = sys_off.ts[i * 2 + 1].sec;
readings[i].phc_ts.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
readings[i].sys_ts2.tv_sec = sys_off.ts[i * 2 + 2].sec;
readings[i].sys_ts2.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
}
return 1;
#else
/* Not available */
return 0;
#endif
}
static int read_phc_user(struct phc_reading *readings, int phc_fd, int n)
{
clockid_t phc_id;
int i;
phc_id = FD_TO_CLOCKID(phc_fd);
for (i = 0; i < n; i++) {
if (clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts1) ||
clock_gettime(phc_id, &readings[i].phc_ts) ||
clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts2)) {
LOG(LOGS_ERR, LOGF_Refclock, "clock_gettime() failed : %s", strerror(errno));
return 0;
}
}
return 1;
}
static void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance)
{
struct ptp_clock_caps caps;
int phc_fd;
char *path;
struct phc_instance *phc;
int phc_fd, rising_edge;
char *path, *s;
path = RCL_GetDriverParameter(instance);
phc_fd = open(path, O_RDONLY);
phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
LOG_FATAL("Could not open PHC");
return 0;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno));
return 0;
phc = MallocNew(struct phc_instance);
phc->fd = phc_fd;
phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
if (phc->extpps) {
s = RCL_GetDriverOption(instance, "pin");
phc->pin = s ? atoi(s) : 0;
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
} else {
phc->pin = phc->channel = 0;
phc->clock = NULL;
}
UTI_FdSetCloexec(phc_fd);
RCL_SetDriverData(instance, (void *)(long)phc_fd);
RCL_SetDriverData(instance, phc);
return 1;
}
static void phc_finalise(RCL_Instance instance)
{
close((long)RCL_GetDriverData(instance));
struct phc_instance *phc;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
HCL_DestroyInstance(phc->clock);
}
close(phc->fd);
Free(phc);
}
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
double local_err;
int channel;
instance = anything;
phc = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return;
}
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
}
static int phc_poll(RCL_Instance instance)
{
struct phc_reading readings[NUM_READINGS];
struct timeval tv;
double offset = 0.0, delay, best_delay = 0.0;
int i, phc_fd, best;
phc_fd = (long)RCL_GetDriverData(instance);
struct phc_instance *phc;
struct timespec phc_ts, sys_ts, local_ts;
double offset, phc_err, local_err;
if (!no_sys_offset_ioctl) {
if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) {
no_sys_offset_ioctl = 1;
return 0;
}
} else {
if (!read_phc_user(readings, phc_fd, NUM_READINGS))
return 0;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &phc_err))
return 0;
if (phc->extpps) {
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
return 0;
}
/* Find the fastest reading */
for (i = 0; i < NUM_READINGS; i++) {
delay = diff_ts(&readings[i].sys_ts2, &readings[i].sys_ts1);
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
if (!i || best_delay > delay) {
best = i;
best_delay = delay;
}
}
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
offset = diff_ts(&readings[best].phc_ts, &readings[best].sys_ts2) + best_delay / 2.0;
tv.tv_sec = readings[best].sys_ts2.tv_sec;
tv.tv_usec = readings[best].sys_ts2.tv_nsec / 1000;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
return RCL_AddSample(instance, &tv, offset, LEAP_Normal);
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {

View File

@@ -59,37 +59,37 @@ static int pps_initialise(RCL_Instance instance) {
fd = open(path, O_RDWR);
if (fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
LOG_FATAL("open() failed on %s", path);
return 0;
}
UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_create() failed on %s", path);
LOG_FATAL("time_pps_create() failed on %s", path);
return 0;
}
if (time_pps_getcap(handle, &mode) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_getcap() failed on %s", path);
LOG_FATAL("time_pps_getcap() failed on %s", path);
return 0;
}
if (time_pps_getparams(handle, &params) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_getparams() failed on %s", path);
LOG_FATAL("time_pps_getparams() failed on %s", path);
return 0;
}
if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT)) {
LOG_FATAL(LOGF_Refclock, "CAPTUREASSERT not supported on %s", path);
LOG_FATAL("CAPTUREASSERT not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR;
} else {
if (!(mode & PPS_CAPTURECLEAR)) {
LOG_FATAL(LOGF_Refclock, "CAPTURECLEAR not supported on %s", path);
LOG_FATAL("CAPTURECLEAR not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTURECLEAR;
@@ -97,7 +97,7 @@ static int pps_initialise(RCL_Instance instance) {
}
if (time_pps_setparams(handle, &params) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_setparams() failed on %s", path);
LOG_FATAL("time_pps_setparams() failed on %s", path);
return 0;
}
@@ -124,7 +124,6 @@ static int pps_poll(RCL_Instance instance)
{
struct pps_instance *pps;
struct timespec ts;
struct timeval tv;
pps_info_t pps_info;
pps_seq_t seq;
@@ -134,7 +133,7 @@ static int pps_poll(RCL_Instance instance)
ts.tv_nsec = 0;
if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) {
LOG(LOGS_ERR, LOGF_Refclock, "time_pps_fetch() failed : %s", strerror(errno));
LOG(LOGS_ERR, "time_pps_fetch() failed : %s", strerror(errno));
return 0;
}
@@ -146,17 +145,15 @@ static int pps_poll(RCL_Instance instance)
ts = pps_info.clear_timestamp;
}
if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%lu.%09lu",
seq, ts.tv_sec, ts.tv_nsec);
if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
DEBUG_LOG("PPS sample ignored seq=%lu ts=%s",
seq, UTI_TimespecToString(&ts));
return 0;
}
pps->last_seq = seq;
tv.tv_sec = ts.tv_sec;
tv.tv_usec = ts.tv_nsec / 1000;
return RCL_AddPulse(instance, &tv, ts.tv_nsec / 1e9);
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
}
RefclockDriver RCL_PPS_driver = {

View File

@@ -69,13 +69,13 @@ static int shm_initialise(RCL_Instance instance) {
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
if (id == -1) {
LOG_FATAL(LOGF_Refclock, "shmget() failed");
LOG_FATAL("shmget() failed");
return 0;
}
shm = (struct shmTime *)shmat(id, 0, 0);
if ((long)shm == -1) {
LOG_FATAL(LOGF_Refclock, "shmat() failed");
LOG_FATAL("shmat() failed");
return 0;
}
@@ -90,7 +90,7 @@ static void shm_finalise(RCL_Instance instance)
static int shm_poll(RCL_Instance instance)
{
struct timeval tv;
struct timespec receive_ts, clock_ts;
struct shmTime t, *shm;
double offset;
@@ -100,24 +100,30 @@ static int shm_poll(RCL_Instance instance)
if ((t.mode == 1 && t.count != shm->count) ||
!(t.mode == 0 || t.mode == 1) || !t.valid) {
DEBUG_LOG(LOGF_Refclock, "SHM sample ignored mode=%d count=%d valid=%d",
DEBUG_LOG("SHM sample ignored mode=%d count=%d valid=%d",
t.mode, t.count, t.valid);
return 0;
}
shm->valid = 0;
tv.tv_sec = t.receiveTimeStampSec;
tv.tv_usec = t.receiveTimeStampUSec;
receive_ts.tv_sec = t.receiveTimeStampSec;
clock_ts.tv_sec = t.clockTimeStampSec;
offset = t.clockTimeStampSec - t.receiveTimeStampSec;
if (t.clockTimeStampNSec / 1000 == t.clockTimeStampUSec &&
t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec)
offset += (t.clockTimeStampNSec - t.receiveTimeStampNSec) * 1e-9;
else
offset += (t.clockTimeStampUSec - t.receiveTimeStampUSec) * 1e-6;
t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec) {
receive_ts.tv_nsec = t.receiveTimeStampNSec;
clock_ts.tv_nsec = t.clockTimeStampNSec;
} else {
receive_ts.tv_nsec = 1000 * t.receiveTimeStampUSec;
clock_ts.tv_nsec = 1000 * t.clockTimeStampUSec;
}
return RCL_AddSample(instance, &tv, offset, t.leap);
UTI_NormaliseTimespec(&clock_ts);
UTI_NormaliseTimespec(&receive_ts);
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
}
RefclockDriver RCL_SHM_driver = {

View File

@@ -57,39 +57,42 @@ struct sock_sample {
int magic;
};
static void read_sample(void *anything)
static void read_sample(int sockfd, int event, void *anything)
{
struct sock_sample sample;
struct timespec ts;
RCL_Instance instance;
int sockfd, s;
int s;
instance = (RCL_Instance)anything;
sockfd = (long)RCL_GetDriverData(instance);
s = recv(sockfd, &sample, sizeof (sample), 0);
if (s < 0) {
LOG(LOGS_ERR, LOGF_Refclock, "Could not read SOCK sample : %s",
LOG(LOGS_ERR, "Could not read SOCK sample : %s",
strerror(errno));
return;
}
if (s != sizeof (sample)) {
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld",
LOG(LOGS_WARN, "Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample));
return;
}
if (sample.magic != SOCK_MAGIC) {
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x",
LOG(LOGS_WARN, "Unexpected magic number in SOCK sample : %x != %x",
sample.magic, SOCK_MAGIC);
return;
}
UTI_TimevalToTimespec(&sample.tv, &ts);
UTI_NormaliseTimespec(&ts);
if (sample.pulse) {
RCL_AddPulse(instance, &sample.tv, sample.offset);
RCL_AddPulse(instance, &ts, sample.offset);
} else {
RCL_AddSample(instance, &sample.tv, sample.offset, sample.leap);
RCL_AddSample(instance, &ts, sample.offset, sample.leap);
}
}
@@ -103,13 +106,13 @@ static int sock_initialise(RCL_Instance instance)
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
LOG_FATAL(LOGF_Refclock, "path %s is too long", path);
LOG_FATAL("path %s is too long", path);
return 0;
}
sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0) {
LOG_FATAL(LOGF_Refclock, "socket() failed");
LOG_FATAL("socket() failed");
return 0;
}
@@ -117,12 +120,12 @@ static int sock_initialise(RCL_Instance instance)
unlink(path);
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
LOG_FATAL(LOGF_Refclock, "bind() failed");
LOG_FATAL("bind() failed");
return 0;
}
RCL_SetDriverData(instance, (void *)(long)sockfd);
SCH_AddInputFileHandler(sockfd, read_sample, instance);
SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance);
return 1;
}
@@ -131,7 +134,7 @@ static void sock_finalise(RCL_Instance instance)
int sockfd;
sockfd = (long)RCL_GetDriverData(instance);
SCH_RemoveInputFileHandler(sockfd);
SCH_RemoveFileHandler(sockfd);
close(sockfd);
}

View File

@@ -49,10 +49,11 @@ static int local_orphan;
static double local_distance;
static NTP_Leap our_leap_status;
static int our_leap_sec;
static int our_tai_offset;
static int our_stratum;
static uint32_t our_ref_id;
static IPAddr our_ref_ip;
struct timeval our_ref_time;
static struct timespec our_ref_time;
static double our_skew;
static double our_residual_freq;
static double our_root_delay;
@@ -111,8 +112,6 @@ static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname;
static time_t last_tz_leap_check;
static NTP_Leap tz_leap;
/* ================================================== */
@@ -136,34 +135,33 @@ static int next_fb_drift;
static SCH_TimeoutID fb_drift_timeout_id;
/* Timestamp of last reference update */
static struct timeval last_ref_update;
static struct timespec last_ref_update;
static double last_ref_update_interval;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when);
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */
static void
handle_slew(struct timeval *raw,
struct timeval *cooked,
handle_slew(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
void *anything)
{
double delta;
struct timeval now;
struct timespec now;
UTI_AdjustTimeval(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
last_ref_update.tv_sec = 0;
last_ref_update.tv_usec = 0;
UTI_ZeroTimespec(&last_ref_update);
} else if (last_ref_update.tv_sec) {
UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
UTI_AdjustTimespec(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
}
/* When the clock was stepped, check if that doesn't change our leap status
@@ -182,11 +180,13 @@ REF_Initialise(void)
FILE *in;
double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm;
int tai_offset;
mode = REF_ModeNormal;
are_we_synchronised = 0;
our_leap_status = LEAP_Unsynchronised;
our_leap_sec = 0;
our_tai_offset = 0;
initialised = 1;
our_root_dispersion = 1.0;
our_root_delay = 1.0;
@@ -206,11 +206,11 @@ REF_Initialise(void)
our_skew = 1.0e-6 * file_skew_ppm;
if (our_skew < MIN_SKEW)
our_skew = MIN_SKEW;
LOG(LOGS_INFO, LOGF_Reference, "Frequency %.3f +/- %.3f ppm read from %s",
LOG(LOGS_INFO, "Frequency %.3f +/- %.3f ppm read from %s",
file_freq_ppm, file_skew_ppm, drift_file);
LCL_SetAbsoluteFrequency(our_frequency_ppm);
} else {
LOG(LOGS_WARN, LOGF_Reference, "Could not read valid frequency and skew from driftfile %s",
LOG(LOGS_WARN, "Could not read valid frequency and skew from driftfile %s",
drift_file);
}
fclose(in);
@@ -220,7 +220,7 @@ REF_Initialise(void)
if (our_frequency_ppm == 0.0) {
our_frequency_ppm = LCL_ReadAbsoluteFrequency();
if (our_frequency_ppm != 0.0) {
LOG(LOGS_INFO, LOGF_Reference, "Initial frequency %.3f ppm", our_frequency_ppm);
LOG(LOGS_INFO, "Initial frequency %.3f ppm", our_frequency_ppm);
}
}
@@ -244,11 +244,11 @@ REF_Initialise(void)
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
if (get_tz_leap(1341014400) == LEAP_InsertSecond &&
get_tz_leap(1356912000) == LEAP_Normal) {
LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname);
if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
} else {
LOG(LOGS_WARN, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname);
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
}
@@ -267,8 +267,7 @@ REF_Initialise(void)
fb_drift_timeout_id = 0;
}
last_ref_update.tv_sec = 0;
last_ref_update.tv_usec = 0;
UTI_ZeroTimespec(&last_ref_update);
last_ref_update_interval = 0.0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -366,7 +365,7 @@ update_drift_file(double freq_ppm, double skew)
out = fopen(temp_drift_file, "w");
if (!out) {
Free(temp_drift_file);
LOG(LOGS_WARN, LOGF_Reference, "Could not open temporary driftfile %s.tmp for writing",
LOG(LOGS_WARN, "Could not open temporary driftfile %s.tmp for writing",
drift_file);
return;
}
@@ -376,7 +375,7 @@ update_drift_file(double freq_ppm, double skew)
r2 = fclose(out);
if (r1 < 0 || r2) {
Free(temp_drift_file);
LOG(LOGS_WARN, LOGF_Reference, "Could not write to temporary driftfile %s.tmp",
LOG(LOGS_WARN, "Could not write to temporary driftfile %s.tmp",
drift_file);
return;
}
@@ -386,8 +385,7 @@ update_drift_file(double freq_ppm, double skew)
if (!stat(drift_file,&buf)) {
if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
chmod(temp_drift_file,buf.st_mode & 0777)) {
LOG(LOGS_WARN, LOGF_Reference,
"Could not change ownership or permissions of temporary driftfile %s.tmp",
LOG(LOGS_WARN, "Could not change ownership or permissions of temporary driftfile %s.tmp",
drift_file);
}
}
@@ -397,7 +395,7 @@ update_drift_file(double freq_ppm, double skew)
if (rename(temp_drift_file,drift_file)) {
unlink(temp_drift_file);
Free(temp_drift_file);
LOG(LOGS_WARN, LOGF_Reference, "Could not replace old driftfile %s with new one %s.tmp",
LOG(LOGS_WARN, "Could not replace old driftfile %s with new one %s.tmp",
drift_file,drift_file);
return;
}
@@ -445,8 +443,8 @@ update_fb_drifts(double freq_ppm, double update_interval)
(freq_ppm - fb_drifts[i].freq);
}
DEBUG_LOG(LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds",
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
DEBUG_LOG("Fallback drift %d updated: %f ppm %f seconds",
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
}
}
@@ -459,7 +457,7 @@ fb_drift_timeout(void *arg)
fb_drift_timeout_id = 0;
DEBUG_LOG(LOGF_Reference, "Fallback drift %d active: %f ppm",
DEBUG_LOG("Fallback drift %d active: %f ppm",
next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq);
LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq);
REF_SetUnsynchronised();
@@ -468,16 +466,16 @@ fb_drift_timeout(void *arg)
/* ================================================== */
static void
schedule_fb_drift(struct timeval *now)
schedule_fb_drift(struct timespec *now)
{
int i, c, secs;
double unsynchronised;
struct timeval when;
struct timespec when;
if (fb_drift_timeout_id)
return; /* already scheduled */
UTI_DiffTimevalsToDouble(&unsynchronised, now, &last_ref_update);
unsynchronised = UTI_DiffTimespecsToDouble(now, &last_ref_update);
for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) {
secs = 1 << i;
@@ -494,14 +492,14 @@ schedule_fb_drift(struct timeval *now)
if (c > next_fb_drift) {
LCL_SetAbsoluteFrequency(fb_drifts[c - fb_drift_min].freq);
next_fb_drift = c;
DEBUG_LOG(LOGF_Reference, "Fallback drift %d set", c);
DEBUG_LOG("Fallback drift %d set", c);
}
if (i <= fb_drift_max) {
next_fb_drift = i;
UTI_AddDoubleToTimeval(now, secs - unsynchronised, &when);
UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i);
DEBUG_LOG("Fallback drift %d scheduled", i);
}
}
@@ -533,8 +531,7 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, LOGF_Reference,
"System clock wrong by %.6f seconds, adjustment started",
LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
-offset);
}
@@ -560,8 +557,7 @@ maybe_log_offset(double offset, time_t now)
-offset, mail_change_threshold);
pclose(p);
} else {
LOG(LOGS_ERR, LOGF_Reference,
"Could not send mail notification to user %s\n",
LOG(LOGS_ERR, "Could not send mail notification to user %s\n",
mail_change_user);
}
}
@@ -596,7 +592,7 @@ is_offset_ok(double offset)
offset = fabs(offset);
if (offset > max_offset) {
LOG(LOGS_WARN, LOGF_Reference,
LOG(LOGS_WARN,
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
if (!max_offset_ignore)
@@ -620,12 +616,18 @@ is_leap_second_day(struct tm *stm) {
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when)
get_tz_leap(time_t when, int *tai_offset)
{
static time_t last_tz_leap_check;
static NTP_Leap tz_leap;
static int tz_tai_offset;
struct tm stm;
time_t t;
char *tz_env, tz_orig[128];
*tai_offset = tz_tai_offset;
/* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600);
if (last_tz_leap_check == when)
@@ -633,12 +635,10 @@ get_tz_leap(time_t when)
last_tz_leap_check = when;
tz_leap = LEAP_Normal;
tz_tai_offset = 0;
stm = *gmtime(&when);
if (!is_leap_second_day(&stm))
return tz_leap;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
if (tz_env) {
@@ -649,6 +649,11 @@ get_tz_leap(time_t when)
setenv("TZ", leap_tzname, 1);
tzset();
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
t = mktime(&stm);
if (t != -1)
tz_tai_offset = t - when + 10;
/* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60;
stm.tm_min = 59;
@@ -670,6 +675,8 @@ get_tz_leap(time_t when)
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
*tai_offset = tz_tai_offset;
return tz_leap;
}
@@ -680,10 +687,13 @@ leap_end_timeout(void *arg)
{
leap_timeout_id = 0;
leap_in_progress = 0;
if (our_tai_offset)
our_tai_offset += our_leap_sec;
our_leap_sec = 0;
if (leap_mode == REF_LeapModeSystem)
LCL_SetSystemLeap(0);
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
if (our_leap_status == LEAP_InsertSecond ||
our_leap_status == LEAP_DeleteSecond)
@@ -699,20 +709,20 @@ leap_start_timeout(void *arg)
switch (leap_mode) {
case REF_LeapModeSystem:
DEBUG_LOG(LOGF_Reference, "Waiting for system clock leap second correction");
DEBUG_LOG("Waiting for system clock leap second correction");
break;
case REF_LeapModeSlew:
LCL_NotifyLeap(our_leap_sec);
LCL_AccumulateOffset(our_leap_sec, 0.0);
LOG(LOGS_WARN, LOGF_Reference, "Adjusting system clock for leap second");
LOG(LOGS_WARN, "Adjusting system clock for leap second");
break;
case REF_LeapModeStep:
LCL_NotifyLeap(our_leap_sec);
LCL_ApplyStepOffset(our_leap_sec);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped for leap second");
LOG(LOGS_WARN, "System clock was stepped for leap second");
break;
case REF_LeapModeIgnore:
LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second");
LOG(LOGS_WARN, "Ignoring leap second");
break;
default:
break;
@@ -727,7 +737,7 @@ leap_start_timeout(void *arg)
static void
set_leap_timeout(time_t now)
{
struct timeval when;
struct timespec when;
/* Stop old timer if there is one */
SCH_RemoveTimeout(leap_timeout_id);
@@ -741,12 +751,12 @@ set_leap_timeout(time_t now)
will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_usec = 0;
when.tv_nsec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
if (leap_mode == REF_LeapModeSystem) {
when.tv_sec--;
when.tv_usec = 500000;
when.tv_nsec = 500000000;
}
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
@@ -757,12 +767,17 @@ set_leap_timeout(time_t now)
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{
int leap_sec;
NTP_Leap tz_leap;
int leap_sec, tai_offset;
leap_sec = 0;
tai_offset = 0;
if (leap_tzname && now && leap == LEAP_Normal)
leap = get_tz_leap(now);
if (leap_tzname && now) {
tz_leap = get_tz_leap(now, &tai_offset);
if (leap == LEAP_Normal)
leap = tz_leap;
}
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
/* Check that leap second is allowed today */
@@ -778,12 +793,14 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
}
}
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
&& !REF_IsLeapSecondClose()) {
our_leap_sec = leap_sec;
our_tai_offset = tai_offset;
switch (leap_mode) {
case REF_LeapModeSystem:
LCL_SetSystemLeap(our_leap_sec);
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
/* Fall through */
case REF_LeapModeSlew:
case REF_LeapModeStep:
@@ -804,7 +821,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
/* ================================================== */
static void
write_log(struct timeval *ref_time, char *ref, int stratum, NTP_Leap leap,
write_log(struct timespec *ref_time, char *ref, int stratum, NTP_Leap leap,
double freq, double skew, double offset, int combined_sources,
double offset_sd, double uncorrected_offset)
{
@@ -827,15 +844,14 @@ special_mode_sync(int valid, double offset)
switch (mode) {
case REF_ModeInitStepSlew:
if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew");
LOG(LOGS_WARN, "No suitable source for initstepslew");
end_ref_mode(0);
break;
}
step = fabs(offset) >= CNF_GetInitStepThreshold();
LOG(LOGS_INFO, LOGF_Reference,
"System's initial offset : %.6f seconds %s of true (%s)",
LOG(LOGS_INFO, "System's initial offset : %.6f seconds %s of true (%s)",
fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew");
if (step)
@@ -849,14 +865,14 @@ special_mode_sync(int valid, double offset)
case REF_ModeUpdateOnce:
case REF_ModePrintOnce:
if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for synchronisation");
LOG(LOGS_WARN, "No suitable source for synchronisation");
end_ref_mode(0);
break;
}
step = mode == REF_ModeUpdateOnce;
LOG(LOGS_INFO, LOGF_Reference, "System clock wrong by %.6f seconds (%s)",
LOG(LOGS_INFO, "System clock wrong by %.6f seconds (%s)",
-offset, step ? "step" : "ignored");
if (step)
@@ -881,7 +897,7 @@ REF_SetReference(int stratum,
int combined_sources,
uint32_t ref_id,
IPAddr *ref_ip,
struct timeval *ref_time,
struct timespec *ref_time,
double offset,
double offset_sd,
double frequency,
@@ -902,7 +918,8 @@ REF_SetReference(int stratum,
double elapsed;
double correction_rate;
double uncorrected_offset, accumulate_offset, step_offset;
struct timeval now, raw_now;
struct timespec now, raw_now;
NTP_int64 ref_fuzz;
assert(initialised);
@@ -929,16 +946,16 @@ REF_SetReference(int stratum,
double t;
t = (skew + skew) / skew; /* Skew shouldn't be zero either */
if ((t < 1.9) || (t > 2.1)) {
LOG(LOGS_WARN, LOGF_Reference, "Bogus skew value encountered");
LOG(LOGS_WARN, "Bogus skew value encountered");
return;
}
}
LCL_ReadRawTime(&raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
UTI_AddDoubleToTimeval(&raw_now, uncorrected_offset, &now);
UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time);
elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
our_offset = offset + elapsed * frequency;
if (!is_offset_ok(our_offset))
@@ -956,7 +973,7 @@ REF_SetReference(int stratum,
our_root_dispersion = root_dispersion;
if (last_ref_update.tv_sec) {
UTI_DiffTimevalsToDouble(&update_interval, &now, &last_ref_update);
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
if (update_interval < 0.0)
update_interval = 0.0;
} else {
@@ -1026,7 +1043,7 @@ REF_SetReference(int stratum,
LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
} else {
DEBUG_LOG(LOGF_Reference, "Skew %f too large to track, offset=%f", skew, accumulate_offset);
DEBUG_LOG("Skew %f too large to track, offset=%f", skew, accumulate_offset);
LCL_AccumulateOffset(accumulate_offset, correction_rate);
@@ -1038,11 +1055,20 @@ REF_SetReference(int stratum,
if (step_offset != 0.0) {
if (LCL_ApplyStepOffset(step_offset))
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
}
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
/* Add a random error of up to one second to the reference time to make it
less useful when disclosed to NTP and cmdmon clients for estimating
receive timestamps in the interleaved symmetric NTP mode */
UTI_GetNtp64Fuzz(&ref_fuzz, 0);
UTI_TimespecToNtp64(&our_ref_time, &ref_fuzz, &ref_fuzz);
UTI_Ntp64ToTimespec(&ref_fuzz, &our_ref_time);
if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
our_ref_time.tv_sec--;
abs_freq_ppm = LCL_ReadAbsoluteFrequency();
write_log(&now,
@@ -1089,7 +1115,7 @@ REF_SetReference(int stratum,
void
REF_SetManualReference
(
struct timeval *ref_time,
struct timespec *ref_time,
double offset,
double frequency,
double skew
@@ -1108,7 +1134,7 @@ void
REF_SetUnsynchronised(void)
{
/* Variables required for logging to statistics log */
struct timeval now, now_raw;
struct timespec now, now_raw;
double uncorrected_offset;
assert(initialised);
@@ -1121,7 +1147,7 @@ REF_SetUnsynchronised(void)
LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &uncorrected_offset, NULL);
UTI_AddDoubleToTimeval(&now_raw, uncorrected_offset, &now);
UTI_AddDoubleToTimespec(&now_raw, uncorrected_offset, &now);
if (fb_drifts) {
schedule_fb_drift(&now);
@@ -1149,12 +1175,12 @@ REF_SetUnsynchronised(void)
void
REF_GetReferenceParams
(
struct timeval *local_time,
struct timespec *local_time,
int *is_synchronised,
NTP_Leap *leap_status,
int *stratum,
uint32_t *ref_id,
struct timeval *ref_time,
struct timespec *ref_time,
double *root_delay,
double *root_dispersion
)
@@ -1164,7 +1190,7 @@ REF_GetReferenceParams
assert(initialised);
if (are_we_synchronised) {
UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
elapsed = UTI_DiffTimespecsToDouble(local_time, &our_ref_time);
dispersion = our_root_dispersion +
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
} else {
@@ -1206,7 +1232,7 @@ REF_GetReferenceParams
*leap_status = LEAP_Normal;
*root_delay = 0.0;
*root_dispersion = LCL_GetSysPrecisionAsQuantum();
*root_dispersion = 0.0;
} else {
@@ -1215,7 +1241,7 @@ REF_GetReferenceParams
*leap_status = LEAP_Unsynchronised;
*stratum = NTP_MAX_STRATUM;
*ref_id = NTP_REFID_UNSYNC;
ref_time->tv_sec = ref_time->tv_usec = 0;
UTI_ZeroTimespec(ref_time);
/* These values seem to be standard for a client, and
any peer or client of ours will ignore them anyway because
we don't claim to be synchronised */
@@ -1230,7 +1256,7 @@ REF_GetReferenceParams
int
REF_GetOurStratum(void)
{
struct timeval now_cooked, ref_time;
struct timespec now_cooked, ref_time;
int synchronised, stratum;
NTP_Leap leap_status;
uint32_t ref_id;
@@ -1303,7 +1329,7 @@ REF_DisableLocal(void)
int REF_IsLeapSecondClose(void)
{
struct timeval now, now_raw;
struct timespec now, now_raw;
time_t t;
if (!our_leap_sec)
@@ -1327,20 +1353,20 @@ int REF_IsLeapSecondClose(void)
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{
struct timeval now_raw, now_cooked;
struct timespec now_raw, now_cooked;
double correction;
int synchronised;
LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &correction, NULL);
UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked);
UTI_AddDoubleToTimespec(&now_raw, correction, &now_cooked);
REF_GetReferenceParams(&now_cooked, &synchronised,
&rep->leap_status, &rep->stratum,
&rep->ref_id, &rep->ref_time,
&rep->root_delay, &rep->root_dispersion);
if (rep->stratum == NTP_MAX_STRATUM)
if (rep->stratum == NTP_MAX_STRATUM && !synchronised)
rep->stratum = 0;
rep->ip_addr.family = IPADDR_UNSPEC;

View File

@@ -99,12 +99,12 @@ extern REF_LeapMode REF_GetLeapMode(void);
extern void REF_GetReferenceParams
(
struct timeval *local_time,
struct timespec *local_time,
int *is_synchronised,
NTP_Leap *leap,
int *stratum,
uint32_t *ref_id,
struct timeval *ref_time,
struct timespec *ref_time,
double *root_delay,
double *root_dispersion
);
@@ -140,7 +140,7 @@ extern void REF_SetReference
int combined_sources,
uint32_t ref_id,
IPAddr *ref_ip,
struct timeval *ref_time,
struct timespec *ref_time,
double offset,
double offset_sd,
double frequency,
@@ -151,7 +151,7 @@ extern void REF_SetReference
extern void REF_SetManualReference
(
struct timeval *ref_time,
struct timespec *ref_time,
double offset,
double frequency,
double skew

131
regress.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011
* Copyright (C) Miroslav Lichvar 2011, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,7 @@
#include "logging.h"
#include "util.h"
#define MAX_POINTS 128
#define MAX_POINTS 64
void
RGR_WeightedRegression
@@ -109,7 +109,7 @@ double
RGR_GetTCoef(int dof)
{
/* Assuming now the 99.95% quantile */
static double coefs[] =
static const float coefs[] =
{ 636.6, 31.6, 12.92, 8.61, 6.869,
5.959, 5.408, 5.041, 4.781, 4.587,
4.437, 4.318, 4.221, 4.140, 4.073,
@@ -132,7 +132,7 @@ RGR_GetTCoef(int dof)
double
RGR_GetChi2Coef(int dof)
{
static double coefs[] = {
static const float coefs[] = {
2.706, 4.605, 6.251, 7.779, 9.236, 10.645, 12.017, 13.362,
14.684, 15.987, 17.275, 18.549, 19.812, 21.064, 22.307, 23.542,
24.769, 25.989, 27.204, 28.412, 29.615, 30.813, 32.007, 33.196,
@@ -150,20 +150,6 @@ RGR_GetChi2Coef(int dof)
}
}
/* ================================================== */
/* Structure used for holding results of each regression */
typedef struct {
double variance;
double slope_sd;
double slope;
double offset;
double offset_sd;
double K2; /* Variance / slope_var */
int n; /* Number of points */
int dof; /* Number of degrees of freedom */
} RegressionResult;
/* ================================================== */
/* Critical value for number of runs of residuals with same sign.
5% critical region for now. */
@@ -299,7 +285,7 @@ RGR_FindBestRegression
n - start <= min_samples) {
if (start != resid_start) {
/* Ignore extra samples in returned nruns */
nruns = n_runs_from_residuals(resid - resid_start + start, n - start);
nruns = n_runs_from_residuals(resid + (start - resid_start), n - start);
}
break;
} else {
@@ -354,7 +340,7 @@ RGR_FindBestRegression
0-521-43108-5). */
static double
find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
{
int u, v, l, r;
double temp;
@@ -417,9 +403,9 @@ find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
static double
find_ordered_entry(double *x, int n, int index)
{
int flags[MAX_POINTS];
char flags[MAX_POINTS];
memset(flags, 0, n * sizeof(int));
memset(flags, 0, n * sizeof (flags[0]));
return find_ordered_entry_with_flags(x, n, index, flags);
}
#endif
@@ -431,9 +417,9 @@ static double
find_median(double *x, int n)
{
int k;
int flags[MAX_POINTS];
char flags[MAX_POINTS];
memset(flags, 0, n*sizeof(int));
memset(flags, 0, n * sizeof (flags[0]));
k = n>>1;
if (n&1) {
return find_ordered_entry_with_flags(x, n, k, flags);
@@ -443,6 +429,19 @@ find_median(double *x, int n)
}
}
/* ================================================== */
double
RGR_FindMedian(double *x, int n)
{
double tmp[MAX_POINTS];
assert(n > 0 && n <= MAX_POINTS);
memcpy(tmp, x, n * sizeof (tmp[0]));
return find_median(tmp, n);
}
/* ================================================== */
/* This function evaluates the equation
@@ -523,7 +522,7 @@ RGR_FindBestRobustRegression
double mx, dx, my, dy;
int nruns = 0;
assert(n < MAX_POINTS);
assert(n <= MAX_POINTS);
if (n < 2) {
return 0;
@@ -580,24 +579,18 @@ RGR_FindBestRobustRegression
Estimate standard deviation of b and expand range about b based
on that. */
sb = sqrt(s2 * W/V);
if (sb > tol) {
incr = 3.0 * sb;
} else {
incr = 3.0 * tol;
}
incr = MAX(sb, tol);
blo = b;
bhi = b;
do {
/* Make sure incr is significant to blo and bhi */
while (bhi + incr == bhi || blo - incr == blo) {
incr *= 2;
}
incr *= 2.0;
/* Give up if the interval is too large */
if (incr > 100.0)
return 0;
blo = b - incr;
bhi = b + incr;
blo -= incr;
bhi += incr;
/* We don't want 'a' yet */
eval_robust_residual(x + start, y + start, n_points, blo, &a, &rlo);
eval_robust_residual(x + start, y + start, n_points, bhi, &a, &rhi);
@@ -608,6 +601,8 @@ RGR_FindBestRobustRegression
/* OK, so the root for b lies in (blo, bhi). Start bisecting */
do {
bmid = 0.5 * (blo + bhi);
if (!(blo < bmid && bmid < bhi))
break;
eval_robust_residual(x + start, y + start, n_points, bmid, &a, &rmid);
if (rmid == 0.0) {
break;
@@ -620,7 +615,7 @@ RGR_FindBestRobustRegression
} else {
assert(0);
}
} while ((bhi - blo) > tol && (bmid - blo) * (bhi - bmid) > 0.0);
} while (bhi - blo > tol);
*b0 = a;
*b1 = bmid;
@@ -653,3 +648,57 @@ RGR_FindBestRobustRegression
}
/* ================================================== */
/* This routine performs linear regression with two independent variables.
It returns non-zero status if there were enough data points and there
was a solution. */
int
RGR_MultipleRegress
(double *x1, /* first independent variable */
double *x2, /* second independent variable */
double *y, /* measured data */
int n, /* number of data points */
/* The results */
double *b2 /* estimated second slope */
/* other values are not needed yet */
)
{
double Sx1, Sx2, Sx1x1, Sx1x2, Sx2x2, Sx1y, Sx2y, Sy;
double U, V, V1, V2, V3;
int i;
if (n < 4)
return 0;
Sx1 = Sx2 = Sx1x1 = Sx1x2 = Sx2x2 = Sx1y = Sx2y = Sy = 0.0;
for (i = 0; i < n; i++) {
Sx1 += x1[i];
Sx2 += x2[i];
Sx1x1 += x1[i] * x1[i];
Sx1x2 += x1[i] * x2[i];
Sx2x2 += x2[i] * x2[i];
Sx1y += x1[i] * y[i];
Sx2y += x2[i] * y[i];
Sy += y[i];
}
U = n * (Sx1x2 * Sx1y - Sx1x1 * Sx2y) +
Sx1 * Sx1 * Sx2y - Sx1 * Sx2 * Sx1y +
Sy * (Sx2 * Sx1x1 - Sx1 * Sx1x2);
V1 = n * (Sx1x2 * Sx1x2 - Sx1x1 * Sx2x2);
V2 = Sx1 * Sx1 * Sx2x2 + Sx2 * Sx2 * Sx1x1;
V3 = -2.0 * Sx1 * Sx2 * Sx1x2;
V = V1 + V2 + V3;
/* Check if there is a (numerically stable) solution */
if (fabs(V) * 1.0e10 <= -V1 + V2 + fabs(V3))
return 0;
*b2 = U / V;
return 1;
}

View File

@@ -119,4 +119,19 @@ RGR_FindBestRobustRegression
int *n_runs,
int *best_start);
int
RGR_MultipleRegress
(double *x1, /* first independent variable */
double *x2, /* second independent variable */
double *y, /* measured data */
int n, /* number of data points */
/* The results */
double *b2 /* estimated second slope */
);
/* Return the median value from an array */
extern double RGR_FindMedian(double *x, int n);
#endif /* GOT_REGRESS_H */

View File

@@ -31,8 +31,6 @@
#include "addressing.h"
#include "ntp.h"
#define REPORT_INVALID_OFFSET 0x80000000
typedef struct {
IPAddr ip_addr;
int stratum;
@@ -53,7 +51,7 @@ typedef struct {
IPAddr ip_addr;
int stratum;
NTP_Leap leap_status;
struct timeval ref_time;
struct timespec ref_time;
double current_correction;
double last_offset;
double rms_offset;
@@ -79,7 +77,7 @@ typedef struct {
} RPT_SourcestatsReport;
typedef struct {
struct timeval ref_time;
struct timespec ref_time;
unsigned short n_samples;
unsigned short n_runs;
unsigned long span_seconds;
@@ -109,7 +107,7 @@ typedef struct {
} RPT_ServerStatsReport;
typedef struct {
struct timeval when;
struct timespec when;
double slewed_offset;
double orig_offset;
double residual;
@@ -133,4 +131,33 @@ typedef struct {
double remaining_time;
} RPT_SmoothingReport;
typedef struct {
IPAddr remote_addr;
IPAddr local_addr;
uint16_t remote_port;
uint8_t leap;
uint8_t version;
uint8_t mode;
uint8_t stratum;
int8_t poll;
int8_t precision;
double root_delay;
double root_dispersion;
uint32_t ref_id;
struct timespec ref_time;
double offset;
double peer_delay;
double peer_dispersion;
double response_time;
double jitter_asymmetry;
uint16_t tests;
int interleaved;
int authenticated;
char tx_tss_char;
char rx_tss_char;
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
} RPT_NTPReport;
#endif /* GOT_REPORTS_H */

8
rtc.c
View File

@@ -98,13 +98,13 @@ get_driftfile_time(void)
static void
apply_driftfile_time(time_t t)
{
struct timeval now;
struct timespec now;
LCL_ReadCookedTime(&now, NULL);
if (now.tv_sec < t) {
if (LCL_ApplyStepOffset(now.tv_sec - t))
LOG(LOGS_INFO, LOGF_Rtc, "System time restored from driftfile");
LOG(LOGS_INFO, "System time restored from driftfile");
}
}
@@ -142,7 +142,7 @@ RTC_Initialise(int initial_set)
if (file_name) {
if (CNF_GetRtcSync()) {
LOG_FATAL(LOGF_Rtc, "rtcfile directive cannot be used with rtcsync");
LOG_FATAL("rtcfile directive cannot be used with rtcsync");
}
if (driver.init) {
@@ -150,7 +150,7 @@ RTC_Initialise(int initial_set)
driver_initialised = 1;
}
} else {
LOG(LOGS_ERR, LOGF_Rtc, "RTC not supported on this operating system");
LOG(LOGS_ERR, "RTC not supported on this operating system");
}
}
}

View File

@@ -50,7 +50,7 @@
static void measurement_timeout(void *any);
static void read_from_device(void *any);
static void read_from_device(int fd_, int event, void *any);
/* ================================================== */
@@ -92,9 +92,8 @@ static double *rtc_trim = NULL;
static time_t rtc_ref;
/* System clock (gettimeofday) samples associated with the above
samples. */
static struct timeval *system_times = NULL;
/* System clock samples associated with the above samples. */
static struct timespec *system_times = NULL;
/* Number of samples currently stored. */
static int n_samples;
@@ -170,7 +169,7 @@ discard_samples(int new_first)
memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timeval));
memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
n_samples = n_to_save;
}
@@ -180,7 +179,7 @@ discard_samples(int new_first)
#define NEW_FIRST_WHEN_FULL 4
static void
accumulate_sample(time_t rtc, struct timeval *sys)
accumulate_sample(time_t rtc, struct timespec *sys)
{
if (n_samples == MAX_SAMPLES) {
@@ -188,6 +187,11 @@ accumulate_sample(time_t rtc, struct timeval *sys)
discard_samples(NEW_FIRST_WHEN_FULL);
}
/* Discard all samples if the RTC was stepped back (not our trim) */
if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) {
DEBUG_LOG("RTC samples discarded");
n_samples = 0;
}
/* Always use most recent sample as reference */
/* use sample only if n_sample is not negative*/
@@ -225,7 +229,7 @@ run_regression(int new_sample,
for (i=0; i<n_samples; i++) {
rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref);
offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
(1.0e-6 * (double) system_times[i].tv_usec) +
(1.0e-9 * system_times[i].tv_nsec) +
rtc_rel[i]);
}
@@ -262,7 +266,7 @@ run_regression(int new_sample,
static void
slew_samples
(struct timeval *raw, struct timeval *cooked,
(struct timespec *raw, struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -278,7 +282,7 @@ slew_samples
}
for (i=0; i<n_samples; i++) {
UTI_AdjustTimeval(system_times + i, cooked, system_times + i, &delta_time,
UTI_AdjustTimespec(system_times + i, cooked, system_times + i, &delta_time,
dfreq, doffset);
}
@@ -290,7 +294,7 @@ slew_samples
coef_gain_rate += dfreq * (1.0 - coef_gain_rate);
}
DEBUG_LOG(LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
DEBUG_LOG("dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
dfreq, doffset,
old_seconds_fast, 1.0e6 * old_gain_rate,
coef_seconds_fast, 1.0e6 * coef_gain_rate);
@@ -367,7 +371,7 @@ t_from_rtc(struct tm *stm) {
diff = t2 - t1;
if (t1 - diff == -1)
DEBUG_LOG(LOGF_RtcLinux, "Could not convert RTC time");
DEBUG_LOG("Could not convert RTC time");
return t1 - diff;
}
@@ -386,7 +390,7 @@ read_hwclock_file(const char *hwclock_file)
in = fopen(hwclock_file, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open %s : %s",
LOG(LOGS_WARN, "Could not open %s : %s",
hwclock_file, strerror(errno));
return;
}
@@ -404,8 +408,7 @@ read_hwclock_file(const char *hwclock_file)
} else if (i == 3 && !strncmp(line, "UTC", 3)) {
rtc_on_utc = 1;
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read RTC LOCAL/UTC setting from %s",
hwclock_file);
LOG(LOGS_WARN, "Could not read RTC LOCAL/UTC setting from %s", hwclock_file);
}
}
@@ -447,8 +450,7 @@ read_coefs_from_file(void)
&file_ref_offset,
&file_rate_ppm) == 4) {
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from %s",
coefs_file_name);
LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
}
fclose(in);
}
@@ -481,7 +483,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
out = fopen(temp_coefs_file_name, "w");
if (!out) {
Free(temp_coefs_file_name);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open temporary RTC file %s.tmp for writing",
LOG(LOGS_WARN, "Could not open temporary RTC file %s.tmp for writing",
coefs_file_name);
return RTC_ST_BADFILE;
}
@@ -492,7 +494,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
r2 = fclose(out);
if (r1 < 0 || r2) {
Free(temp_coefs_file_name);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not write to temporary RTC file %s.tmp",
LOG(LOGS_WARN, "Could not write to temporary RTC file %s.tmp",
coefs_file_name);
return RTC_ST_BADFILE;
}
@@ -502,7 +504,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
if (!stat(coefs_file_name,&buf)) {
if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) ||
chmod(temp_coefs_file_name,buf.st_mode & 0777)) {
LOG(LOGS_WARN, LOGF_RtcLinux,
LOG(LOGS_WARN,
"Could not change ownership or permissions of temporary RTC file %s.tmp",
coefs_file_name);
}
@@ -513,7 +515,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
if (rename(temp_coefs_file_name,coefs_file_name)) {
unlink(temp_coefs_file_name);
Free(temp_coefs_file_name);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not replace old RTC file %s.tmp with new one %s",
LOG(LOGS_WARN, "Could not replace old RTC file %s.tmp with new one %s",
coefs_file_name, coefs_file_name);
return RTC_ST_BADFILE;
}
@@ -534,7 +536,7 @@ RTC_Linux_Initialise(void)
{
rtc_sec = MallocArray(time_t, MAX_SAMPLES);
rtc_trim = MallocArray(double, MAX_SAMPLES);
system_times = MallocArray(struct timeval, MAX_SAMPLES);
system_times = MallocArray(struct timespec, MAX_SAMPLES);
/* Setup details depending on configuration options */
setup_config();
@@ -546,7 +548,7 @@ RTC_Linux_Initialise(void)
fd = open (CNF_GetRtcDevice(), O_RDWR);
if (fd < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open RTC device %s : %s",
LOG(LOGS_ERR, "Could not open RTC device %s : %s",
CNF_GetRtcDevice(), strerror(errno));
return 0;
}
@@ -564,7 +566,7 @@ RTC_Linux_Initialise(void)
operating_mode = OM_NORMAL;
/* Register file handler */
SCH_AddInputFileHandler(fd, read_from_device, NULL);
SCH_AddFileHandler(fd, SCH_FILE_INPUT, read_from_device, NULL);
/* Register slew handler */
LCL_AddParameterChangeHandler(slew_samples, NULL);
@@ -585,7 +587,7 @@ RTC_Linux_Finalise(void)
/* Remove input file handler */
if (fd >= 0) {
SCH_RemoveInputFileHandler(fd);
SCH_RemoveFileHandler(fd);
close(fd);
/* Save the RTC data */
@@ -607,16 +609,14 @@ switch_interrupts(int onoff)
if (onoff) {
status = ioctl(fd, RTC_UIE_ON, 0);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
"enable", strerror(errno));
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "enable", strerror(errno));
return;
}
skip_interrupts = 1;
} else {
status = ioctl(fd, RTC_UIE_OFF, 0);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
"disable", strerror(errno));
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "disable", strerror(errno));
return;
}
}
@@ -654,7 +654,7 @@ set_rtc(time_t new_rtc_time)
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not set RTC time");
LOG(LOGS_ERR, "Could not set RTC time");
}
}
@@ -697,10 +697,10 @@ handle_initial_trim(void)
sys_error_now = rtc_error_now - coef_seconds_fast;
LCL_AccumulateOffset(sys_error_now, 0.0);
LOG(LOGS_INFO, LOGF_RtcLinux, "System clock off from RTC by %f seconds (slew)",
LOG(LOGS_INFO, "System clock off from RTC by %f seconds (slew)",
sys_error_now);
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid rtcfile coefficients");
LOG(LOGS_WARN, "No valid rtcfile coefficients");
}
coefs_valid = 0;
@@ -725,7 +725,7 @@ handle_relock_after_trim(void)
if (valid) {
write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
} else {
DEBUG_LOG(LOGF_RtcLinux, "Could not do regression after trim");
DEBUG_LOG("Could not do regression after trim");
}
coefs_valid = 0;
@@ -758,7 +758,7 @@ maybe_autotrim(void)
/* ================================================== */
static void
process_reading(time_t rtc_time, struct timeval *system_time)
process_reading(time_t rtc_time, struct timespec *system_time)
{
double rtc_fast;
@@ -791,7 +791,7 @@ process_reading(time_t rtc_time, struct timeval *system_time)
if (logfileid != -1) {
rtc_fast = (double)(rtc_time - system_time->tv_sec) - 1.0e-6 * (double) system_time->tv_usec;
rtc_fast = (rtc_time - system_time->tv_sec) - 1.0e-9 * system_time->tv_nsec;
LOG_FileWrite(logfileid, "%s %14.6f %1d %14.6f %12.3f %2d %2d %4d",
UTI_TimeToLogForm(system_time->tv_sec),
@@ -805,11 +805,11 @@ process_reading(time_t rtc_time, struct timeval *system_time)
/* ================================================== */
static void
read_from_device(void *any)
read_from_device(int fd_, int event, void *any)
{
int status;
unsigned long data;
struct timeval sys_time;
struct timespec sys_time;
struct rtc_time rtc_raw;
struct tm rtc_tm;
time_t rtc_t;
@@ -820,8 +820,8 @@ read_from_device(void *any)
if (status < 0) {
/* This looks like a bad error : the file descriptor was indicating it was
* ready to read but we couldn't read anything. Give up. */
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
SCH_RemoveInputFileHandler(fd);
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
SCH_RemoveFileHandler(fd);
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
close(fd);
fd = -1;
@@ -844,12 +844,12 @@ read_from_device(void *any)
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
error = 1;
goto turn_off_interrupt;
}
/* Convert RTC time into a struct timeval */
/* Convert RTC time into a struct timespec */
rtc_tm.tm_sec = rtc_raw.tm_sec;
rtc_tm.tm_min = rtc_raw.tm_min;
rtc_tm.tm_hour = rtc_raw.tm_hour;
@@ -885,7 +885,7 @@ turn_off_interrupt:
switch (operating_mode) {
case OM_INITIAL:
if (error) {
DEBUG_LOG(LOGF_RtcLinux, "Could not complete initial step due to errors");
DEBUG_LOG("Could not complete initial step due to errors");
operating_mode = OM_NORMAL;
(after_init_hook)(after_init_hook_arg);
@@ -898,7 +898,7 @@ turn_off_interrupt:
case OM_AFTERTRIM:
if (error) {
DEBUG_LOG(LOGF_RtcLinux, "Could not complete after trim relock due to errors");
DEBUG_LOG("Could not complete after trim relock due to errors");
operating_mode = OM_NORMAL;
switch_interrupts(0);
@@ -978,7 +978,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
struct tm rtc_tm;
time_t rtc_t;
double accumulated_error, sys_offset;
struct timeval new_sys_time, old_sys_time;
struct timespec new_sys_time, old_sys_time;
coefs_file_name = CNF_GetRtcFile();
@@ -1032,21 +1032,21 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
new_sys_time.tv_sec = rtc_t;
/* Average error in the RTC reading */
new_sys_time.tv_usec = 500000;
new_sys_time.tv_nsec = 500000000;
UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
return 0;
}
UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time);
sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) {
if (LCL_ApplyStepOffset(sys_offset))
LOG(LOGS_INFO, LOGF_RtcLinux, "System time set from RTC");
LOG(LOGS_INFO, "System time set from RTC");
}
} else {
return 0;
@@ -1064,7 +1064,7 @@ int
RTC_Linux_GetReport(RPT_RTC_Report *report)
{
report->ref_time.tv_sec = coef_ref_time;
report->ref_time.tv_usec = 0;
report->ref_time.tv_nsec = 0;
report->n_samples = n_samples;
report->n_runs = n_runs;
if (n_samples > 1) {
@@ -1083,8 +1083,7 @@ RTC_Linux_GetReport(RPT_RTC_Report *report)
int
RTC_Linux_Trim(void)
{
struct timeval now;
struct timespec now;
/* Remember the slope coefficient - we won't be able to determine a
good one in a few seconds when we determine the new offset! */
@@ -1092,7 +1091,7 @@ RTC_Linux_Trim(void)
if (fabs(coef_seconds_fast) > 1.0) {
LOG(LOGS_INFO, LOGF_RtcLinux, "RTC wrong by %.3f seconds (step)",
LOG(LOGS_INFO, "RTC wrong by %.3f seconds (step)",
coef_seconds_fast);
/* Do processing to set clock. Let R be the value we set the
@@ -1114,7 +1113,7 @@ RTC_Linux_Trim(void)
/* Estimate the offset in case writertc is called or chronyd
is terminated during rapid sampling */
coef_seconds_fast = -now.tv_usec / 1e6 + 0.5;
coef_seconds_fast = -now.tv_nsec / 1.0e9 + 0.5;
coef_ref_time = now.tv_sec;
/* And start rapid sampling, interrupts on now */

304
sched.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2013-2015
* Copyright (C) Miroslav Lichvar 2011, 2013-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -44,17 +44,6 @@ static int initialised = 0;
/* ================================================== */
/* Variables to handle the capability to dispatch on particular file
handles becoming readable */
/* Each bit set in this fd set corresponds to a read descriptor that
we are watching and with which we have a handler associated in the
file_handlers array */
static fd_set read_fds;
/* This is the number of bits that we have set in read_fds */
static unsigned int n_read_fds;
/* One more than the highest file descriptor that is registered */
static unsigned int one_highest_fd;
@@ -67,12 +56,13 @@ static unsigned int one_highest_fd;
typedef struct {
SCH_FileHandler handler;
SCH_ArbitraryArgument arg;
int events;
} FileHandlerEntry;
static ARR_Instance file_handlers;
/* Timestamp when last select() returned */
static struct timeval last_select_ts, last_select_ts_raw;
static struct timespec last_select_ts, last_select_ts_raw;
static double last_select_ts_err;
/* ================================================== */
@@ -83,7 +73,7 @@ typedef struct _TimerQueueEntry
{
struct _TimerQueueEntry *next; /* Forward and back links in the list */
struct _TimerQueueEntry *prev;
struct timeval tv; /* Local system time at which the
struct timespec ts; /* Local system time at which the
timeout is to expire. Clearly this
must be in terms of what the
operating system thinks of as
@@ -111,7 +101,7 @@ static SCH_TimeoutID next_tqe_id;
static TimerQueueEntry *tqe_free_list = NULL;
/* Timestamp when was last timeout dispatched for each class */
static struct timeval last_class_dispatch[SCH_NumberOfClasses];
static struct timespec last_class_dispatch[SCH_NumberOfClasses];
/* ================================================== */
@@ -120,8 +110,8 @@ static int need_to_exit;
/* ================================================== */
static void
handle_slew(struct timeval *raw,
struct timeval *cooked,
handle_slew(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -132,9 +122,6 @@ handle_slew(struct timeval *raw,
void
SCH_Initialise(void)
{
FD_ZERO(&read_fds);
n_read_fds = 0;
file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry));
n_timer_queue_entries = 0;
@@ -166,73 +153,85 @@ SCH_Finalise(void) {
/* ================================================== */
void
SCH_AddInputFileHandler
(int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
SCH_AddFileHandler
(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
{
FileHandlerEntry *ptr;
assert(initialised);
assert(events);
assert(fd >= 0);
if (fd >= FD_SETSIZE)
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
LOG_FATAL("Too many file descriptors");
/* Resize the array if the descriptor is highest so far */
while (ARR_GetSize(file_handlers) <= fd) {
ptr = ARR_GetNewElement(file_handlers);
ptr->handler = NULL;
ptr->arg = NULL;
ptr->events = 0;
}
ptr = ARR_GetElement(file_handlers, fd);
/* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests
a bug somewhere else in the program. */
if (FD_ISSET(fd, &read_fds))
assert(0);
assert(!ptr->handler);
++n_read_fds;
if (ARR_GetSize(file_handlers) < fd + 1)
ARR_SetSize(file_handlers, fd + 1);
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
ptr->handler = handler;
ptr->arg = arg;
ptr->events = events;
FD_SET(fd, &read_fds);
if ((fd + 1) > one_highest_fd) {
if (one_highest_fd < fd + 1)
one_highest_fd = fd + 1;
}
}
/* ================================================== */
void
SCH_RemoveInputFileHandler(int fd)
SCH_RemoveFileHandler(int fd)
{
int fds_left, fd_to_check;
FileHandlerEntry *ptr;
assert(initialised);
ptr = ARR_GetElement(file_handlers, fd);
/* Check that a handler was registered for the fd in question */
if (!FD_ISSET(fd, &read_fds))
assert(0);
assert(ptr->handler);
--n_read_fds;
FD_CLR(fd, &read_fds);
ptr->handler = NULL;
ptr->arg = NULL;
ptr->events = 0;
/* Find new highest file descriptor */
fds_left = n_read_fds;
fd_to_check = 0;
while (fds_left > 0) {
if (FD_ISSET(fd_to_check, &read_fds)) {
--fds_left;
}
++fd_to_check;
while (one_highest_fd > 0) {
ptr = ARR_GetElement(file_handlers, one_highest_fd - 1);
if (ptr->handler)
break;
one_highest_fd--;
}
one_highest_fd = fd_to_check;
}
/* ================================================== */
void
SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw)
SCH_SetFileHandlerEvents(int fd, int events)
{
FileHandlerEntry *ptr;
assert(events);
ptr = ARR_GetElement(file_handlers, fd);
ptr->events = events;
}
/* ================================================== */
void
SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw)
{
if (cooked) {
*cooked = last_select_ts;
@@ -299,7 +298,7 @@ try_again:
/* ================================================== */
SCH_TimeoutID
SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{
TimerQueueEntry *new_tqe;
TimerQueueEntry *ptr;
@@ -311,12 +310,12 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler;
new_tqe->arg = arg;
new_tqe->tv = *tv;
new_tqe->ts = *ts;
new_tqe->class = SCH_ReservedTimeoutValue;
/* Now work out where to insert the new entry in the list */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
if (UTI_CompareTimevals(&new_tqe->tv, &ptr->tv) == -1) {
if (UTI_CompareTimespecs(&new_tqe->ts, &ptr->ts) == -1) {
/* If the new entry comes before the current pointer location in
the list, we want to insert the new entry just before ptr. */
break;
@@ -343,15 +342,15 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
SCH_TimeoutID
SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{
struct timeval now, then;
struct timespec now, then;
assert(initialised);
assert(delay >= 0.0);
LCL_ReadRawTime(&now);
UTI_AddDoubleToTimeval(&now, delay, &then);
if (UTI_CompareTimevals(&now, &then) > 0) {
LOG_FATAL(LOGF_Scheduler, "Timeout overflow");
UTI_AddDoubleToTimespec(&now, delay, &then);
if (UTI_CompareTimespecs(&now, &then) > 0) {
LOG_FATAL("Timeout overflow");
}
return SCH_AddTimeout(&then, handler, arg);
@@ -367,7 +366,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
{
TimerQueueEntry *new_tqe;
TimerQueueEntry *ptr;
struct timeval now;
struct timespec now;
double diff, r;
double new_min_delay;
@@ -376,10 +375,10 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
assert(class < SCH_NumberOfClasses);
if (randomness > 0.0) {
uint16_t rnd;
uint32_t rnd;
UTI_GetRandomBytes(&rnd, sizeof (rnd));
r = rnd / (double)0xffff * randomness + 1.0;
r = rnd * (randomness / (uint32_t)-1) + 1.0;
min_delay *= r;
separation *= r;
}
@@ -388,7 +387,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
new_min_delay = min_delay;
/* Check the separation from the last dispatched timeout */
UTI_DiffTimevalsToDouble(&diff, &now, &last_class_dispatch[class]);
diff = UTI_DiffTimespecsToDouble(&now, &last_class_dispatch[class]);
if (diff < separation && diff >= 0.0 && diff + new_min_delay < separation) {
new_min_delay = separation - diff;
}
@@ -397,7 +396,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
if necessary to keep at least the separation away */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
if (ptr->class == class) {
UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now);
diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
if (new_min_delay > diff) {
if (new_min_delay - diff < separation) {
new_min_delay = diff + separation;
@@ -411,7 +410,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
}
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now);
diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
if (diff > new_min_delay) {
break;
}
@@ -423,7 +422,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler;
new_tqe->arg = arg;
UTI_AddDoubleToTimeval(&now, new_min_delay, &new_tqe->tv);
UTI_AddDoubleToTimespec(&now, new_min_delay, &new_tqe->ts);
new_tqe->class = class;
new_tqe->next = ptr;
@@ -477,7 +476,7 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
completed). */
static void
dispatch_timeouts(struct timeval *now) {
dispatch_timeouts(struct timespec *now) {
TimerQueueEntry *ptr;
SCH_TimeoutHandler handler;
SCH_ArbitraryArgument arg;
@@ -487,7 +486,7 @@ dispatch_timeouts(struct timeval *now) {
LCL_ReadRawTime(now);
if (!(n_timer_queue_entries > 0 &&
UTI_CompareTimevals(now, &(timer_queue.next->tv)) >= 0)) {
UTI_CompareTimespecs(now, &timer_queue.next->ts) >= 0)) {
break;
}
@@ -513,42 +512,56 @@ dispatch_timeouts(struct timeval *now) {
more time than was delay of a scheduled timeout. */
if (n_done > n_timer_queue_entries * 4 &&
n_done > n_entries_on_start * 4) {
LOG_FATAL(LOGF_Scheduler, "Possible infinite loop in scheduling");
LOG_FATAL("Possible infinite loop in scheduling");
}
}
}
/* ================================================== */
/* nfh is the number of bits set in fhs */
/* nfd is the number of bits set in all fd_sets */
static void
dispatch_filehandlers(int nfh, fd_set *fhs)
dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
{
FileHandlerEntry *ptr;
int fh = 0;
int fd;
while (nfh > 0) {
if (FD_ISSET(fh, fhs)) {
for (fd = 0; nfd && fd < one_highest_fd; fd++) {
if (except_fds && FD_ISSET(fd, except_fds)) {
/* This descriptor has an exception, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
nfd--;
/* This descriptor can be read from, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fh);
(ptr->handler)(ptr->arg);
/* Decrement number of readable files still to find */
--nfh;
/* Don't try to read from it now */
if (read_fds && FD_ISSET(fd, read_fds)) {
FD_CLR(fd, read_fds);
nfd--;
}
}
++fh;
}
if (read_fds && FD_ISSET(fd, read_fds)) {
/* This descriptor can be read from, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
nfd--;
}
if (write_fds && FD_ISSET(fd, write_fds)) {
/* This descriptor can be written to, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
(ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
nfd--;
}
}
}
/* ================================================== */
static void
handle_slew(struct timeval *raw,
struct timeval *cooked,
handle_slew(struct timespec *raw,
struct timespec *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
@@ -566,17 +579,69 @@ handle_slew(struct timeval *raw,
/* If a step change occurs, just shift all raw time stamps by the offset */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
UTI_AddDoubleToTimeval(&ptr->tv, -doffset, &ptr->tv);
UTI_AddDoubleToTimespec(&ptr->ts, -doffset, &ptr->ts);
}
for (i = 0; i < SCH_NumberOfClasses; i++) {
UTI_AddDoubleToTimeval(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]);
UTI_AddDoubleToTimespec(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]);
}
UTI_AddDoubleToTimeval(&last_select_ts_raw, -doffset, &last_select_ts_raw);
UTI_AddDoubleToTimespec(&last_select_ts_raw, -doffset, &last_select_ts_raw);
}
UTI_AdjustTimeval(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset);
UTI_AdjustTimespec(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset);
}
/* ================================================== */
static void
fill_fd_sets(fd_set **read_fds, fd_set **write_fds, fd_set **except_fds)
{
FileHandlerEntry *handlers;
fd_set *rd, *wr, *ex;
int i, n, events;
n = ARR_GetSize(file_handlers);
handlers = ARR_GetElements(file_handlers);
rd = wr = ex = NULL;
for (i = 0; i < n; i++) {
events = handlers[i].events;
if (!events)
continue;
if (events & SCH_FILE_INPUT) {
if (!rd) {
rd = *read_fds;
FD_ZERO(rd);
}
FD_SET(i, rd);
}
if (events & SCH_FILE_OUTPUT) {
if (!wr) {
wr = *write_fds;
FD_ZERO(wr);
}
FD_SET(i, wr);
}
if (events & SCH_FILE_EXCEPTION) {
if (!ex) {
ex = *except_fds;
FD_ZERO(ex);
}
FD_SET(i, ex);
}
}
if (!rd)
*read_fds = NULL;
if (!wr)
*write_fds = NULL;
if (!ex)
*except_fds = NULL;
}
/* ================================================== */
@@ -584,45 +649,47 @@ handle_slew(struct timeval *raw,
#define JUMP_DETECT_THRESHOLD 10
static int
check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
struct timeval *orig_select_tv,
struct timeval *rem_select_tv)
{
struct timeval elapsed_min, elapsed_max;
struct timespec elapsed_min, elapsed_max, orig_select_ts, rem_select_ts;
double step, elapsed;
UTI_TimevalToTimespec(orig_select_tv, &orig_select_ts);
/* Get an estimate of the time spent waiting in the select() call. On some
systems (e.g. Linux) the timeout timeval is modified to return the
remaining time, use that information. */
if (timeout) {
elapsed_max = elapsed_min = *orig_select_tv;
elapsed_max = elapsed_min = orig_select_ts;
} else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
(rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
UTI_DiffTimevals(&elapsed_min, orig_select_tv, rem_select_tv);
UTI_TimevalToTimespec(rem_select_tv, &rem_select_ts);
UTI_DiffTimespecs(&elapsed_min, &orig_select_ts, &rem_select_ts);
elapsed_max = elapsed_min;
} else {
if (rem_select_tv)
elapsed_max = *orig_select_tv;
elapsed_max = orig_select_ts;
else
UTI_DiffTimevals(&elapsed_max, raw, prev_raw);
elapsed_min.tv_sec = 0;
elapsed_min.tv_usec = 0;
UTI_DiffTimespecs(&elapsed_max, raw, prev_raw);
UTI_ZeroTimespec(&elapsed_min);
}
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
raw->tv_sec + JUMP_DETECT_THRESHOLD) {
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected!");
LOG(LOGS_WARN, "Backward time jump detected!");
} else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
raw->tv_sec) {
LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!");
LOG(LOGS_WARN, "Forward time jump detected!");
} else {
return 1;
}
UTI_DiffTimevalsToDouble(&step, &last_select_ts_raw, raw);
UTI_TimevalToDouble(&elapsed_min, &elapsed);
step = UTI_DiffTimespecsToDouble(&last_select_ts_raw, raw);
elapsed = UTI_TimespecToDouble(&elapsed_min);
step += elapsed;
/* Cooked time may no longer be valid after dispatching the handlers */
@@ -636,10 +703,11 @@ check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
void
SCH_MainLoop(void)
{
fd_set rd;
fd_set read_fds, write_fds, except_fds;
fd_set *p_read_fds, *p_write_fds, *p_except_fds;
int status, errsv;
struct timeval tv, saved_tv, *ptv;
struct timeval now, saved_now, cooked;
struct timespec ts, now, saved_now, cooked;
double err;
assert(initialised);
@@ -655,28 +723,28 @@ SCH_MainLoop(void)
/* Check whether there is a timeout and set it up */
if (n_timer_queue_entries > 0) {
UTI_DiffTimespecs(&ts, &timer_queue.next->ts, &now);
assert(ts.tv_sec > 0 || ts.tv_nsec > 0);
UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now);
UTI_TimespecToTimeval(&ts, &tv);
ptv = &tv;
assert(tv.tv_sec > 0 || tv.tv_usec > 0);
saved_tv = tv;
} else {
ptv = NULL;
/* This is needed to fix a compiler warning */
saved_tv.tv_sec = 0;
saved_tv.tv_sec = saved_tv.tv_usec = 0;
}
p_read_fds = &read_fds;
p_write_fds = &write_fds;
p_except_fds = &except_fds;
fill_fd_sets(&p_read_fds, &p_write_fds, &p_except_fds);
/* if there are no file descriptors being waited on and no
timeout set, this is clearly ridiculous, so stop the run */
if (!ptv && !n_read_fds) {
LOG_FATAL(LOGF_Scheduler, "Nothing to do");
}
if (!ptv && !p_read_fds && !p_write_fds)
LOG_FATAL("Nothing to do");
/* Copy current set of read file descriptors */
memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
status = select(one_highest_fd, &rd, NULL, NULL, ptv);
status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
errsv = errno;
LCL_ReadRawTime(&now);
@@ -694,13 +762,11 @@ SCH_MainLoop(void)
if (status < 0) {
if (!need_to_exit && errsv != EINTR) {
LOG_FATAL(LOGF_Scheduler, "select() failed : %s", strerror(errsv));
LOG_FATAL("select() failed : %s", strerror(errsv));
}
} else if (status > 0) {
/* A file descriptor is ready to read */
dispatch_filehandlers(status, &rd);
/* A file descriptor is ready for input or output */
dispatch_filehandlers(status, p_read_fds, p_write_fds, p_except_fds);
} else {
/* No descriptors readable, timeout must have elapsed.
Therefore, tv must be non-null */

20
sched.h
View File

@@ -40,7 +40,7 @@ typedef enum {
} SCH_TimeoutClass;
typedef void* SCH_ArbitraryArgument;
typedef void (*SCH_FileHandler)(SCH_ArbitraryArgument);
typedef void (*SCH_FileHandler)(int fd, int event, SCH_ArbitraryArgument);
typedef void (*SCH_TimeoutHandler)(SCH_ArbitraryArgument);
/* Exported functions */
@@ -51,19 +51,21 @@ extern void SCH_Initialise(void);
/* Finalisation function for the module */
extern void SCH_Finalise(void);
/* File events */
#define SCH_FILE_INPUT 1
#define SCH_FILE_OUTPUT 2
#define SCH_FILE_EXCEPTION 4
/* Register a handler for when select goes true on a file descriptor */
extern void SCH_AddInputFileHandler
(int fd, /* The file descriptor */
SCH_FileHandler, /* The handler routine */
SCH_ArbitraryArgument /* An arbitrary passthrough argument to the handler */
);
extern void SCH_RemoveInputFileHandler(int fd);
extern void SCH_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
extern void SCH_RemoveFileHandler(int fd);
extern void SCH_SetFileHandlerEvents(int fd, int events);
/* Get the time stamp taken after a file descriptor became ready or a timeout expired */
extern void SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw);
extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);
/* This queues a timeout to elapse at a given (raw) local time */
extern SCH_TimeoutID SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler, SCH_ArbitraryArgument);
extern SCH_TimeoutID SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg);
/* This queues a timeout to elapse at a given delta time relative to the current (raw) time */
extern SCH_TimeoutID SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler, SCH_ArbitraryArgument);

View File

@@ -93,17 +93,17 @@ static double max_freq;
/* Frequency offset, time offset and the time of the last smoothing update */
static double smooth_freq;
static double smooth_offset;
static struct timeval last_update;
static struct timespec last_update;
static void
get_smoothing(struct timeval *now, double *poffset, double *pfreq,
get_smoothing(struct timespec *now, double *poffset, double *pfreq,
double *pwander)
{
double elapsed, length, offset, freq, wander;
int i;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
elapsed = UTI_DiffTimespecsToDouble(now, &last_update);
offset = smooth_offset;
freq = smooth_freq;
@@ -208,13 +208,13 @@ update_stages(void)
stages[2].length = l3;
for (i = 0; i < NUM_STAGES; i++) {
DEBUG_LOG(LOGF_Smooth, "Smooth stage %d wander %e length %f",
DEBUG_LOG("Smooth stage %d wander %e length %f",
i + 1, stages[i].wander, stages[i].length);
}
}
static void
update_smoothing(struct timeval *now, double offset, double freq)
update_smoothing(struct timespec *now, double offset, double freq)
{
/* Don't accept offset/frequency until the clock has stabilized */
if (locked) {
@@ -230,11 +230,11 @@ update_smoothing(struct timeval *now, double offset, double freq)
update_stages();
DEBUG_LOG(LOGF_Smooth, "Smooth offset %e freq %e", smooth_offset, smooth_freq);
DEBUG_LOG("Smooth offset %e freq %e", smooth_offset, smooth_freq);
}
static void
handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
double delta;
@@ -246,7 +246,7 @@ handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
update_smoothing(cooked, doffset, dfreq);
}
UTI_AdjustTimeval(&last_update, cooked, &last_update, &delta, dfreq, doffset);
UTI_AdjustTimespec(&last_update, cooked, &last_update, &delta, dfreq, doffset);
}
void SMT_Initialise(void)
@@ -277,7 +277,7 @@ int SMT_IsEnabled(void)
}
double
SMT_GetOffset(struct timeval *now)
SMT_GetOffset(struct timespec *now)
{
double offset, freq;
@@ -290,19 +290,19 @@ SMT_GetOffset(struct timeval *now)
}
void
SMT_Activate(struct timeval *now)
SMT_Activate(struct timespec *now)
{
if (!enabled || !locked)
return;
LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated%s", leap_only_mode ?
LOG(LOGS_INFO, "Time smoothing activated%s", leap_only_mode ?
" (leap seconds only)" : "");
locked = 0;
last_update = *now;
}
void
SMT_Reset(struct timeval *now)
SMT_Reset(struct timespec *now)
{
int i;
@@ -318,7 +318,7 @@ SMT_Reset(struct timeval *now)
}
void
SMT_Leap(struct timeval *now, int leap)
SMT_Leap(struct timespec *now, int leap)
{
/* When the leap-only mode is disabled, the leap second will be accumulated
in handle_slew() as a normal offset */
@@ -329,7 +329,7 @@ SMT_Leap(struct timeval *now, int leap)
}
int
SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now)
SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timespec *now)
{
double length, elapsed;
int i;
@@ -346,7 +346,7 @@ SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now)
report->freq_ppm *= -1.0e6;
report->wander_ppm *= -1.0e6;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
elapsed = UTI_DiffTimespecsToDouble(now, &last_update);
if (!locked && elapsed >= 0.0) {
for (i = 0, length = 0.0; i < NUM_STAGES; i++)
length += stages[i].length;

View File

@@ -35,14 +35,14 @@ extern void SMT_Finalise(void);
extern int SMT_IsEnabled(void);
extern double SMT_GetOffset(struct timeval *now);
extern double SMT_GetOffset(struct timespec *now);
extern void SMT_Activate(struct timeval *now);
extern void SMT_Activate(struct timespec *now);
extern void SMT_Reset(struct timeval *now);
extern void SMT_Reset(struct timespec *now);
extern void SMT_Leap(struct timeval *now, int leap);
extern void SMT_Leap(struct timespec *now, int leap);
extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now);
extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timespec *now);
#endif

290
sources.c
View File

@@ -56,7 +56,7 @@ static int initialised = 0;
struct SelectInfo {
int stratum;
int select_ok;
double variance;
double std_dev;
double root_distance;
double lo_limit;
double hi_limit;
@@ -71,12 +71,12 @@ typedef enum {
SRC_UNSELECTABLE, /* Has noselect option set */
SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
SRC_STALE, /* Has older samples than others */
SRC_ORPHAN, /* Has stratum equal or larger than orphan stratum */
SRC_UNTRUSTED, /* Overlaps trusted sources */
SRC_FALSETICKER, /* Doesn't agree with others */
SRC_JITTERY, /* Scatter worse than other's dispersion (not used) */
SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */
SRC_NONPREFERRED, /* Others have prefer option */
SRC_WAITS_UPDATE, /* No updates, selection postponed */
@@ -159,6 +159,7 @@ static int selected_source_index; /* Which source index is currently
#define DISTANT_PENALTY 32
static double max_distance;
static double max_jitter;
static double reselect_distance;
static double stratum_weight;
static double combine_limit;
@@ -167,7 +168,7 @@ static double combine_limit;
/* Forward prototype */
static void
slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq,
slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
static void
add_dispersion(double dispersion, void *anything);
@@ -184,6 +185,7 @@ void SRC_Initialise(void) {
max_n_sources = 0;
selected_source_index = INVALID_SOURCE;
max_distance = CNF_GetMaxDistance();
max_jitter = CNF_GetMaxJitter();
reselect_distance = CNF_GetReselectDistance();
stratum_weight = CNF_GetStratumWeight();
combine_limit = CNF_GetCombineLimit();
@@ -309,22 +311,12 @@ SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr)
}
/* ================================================== */
/* Function to get the range of frequencies, relative to the given
source, that we believe the local clock lies within. The return
values are in terms of the number of seconds fast (+ve) or slow
(-ve) relative to the source that the local clock becomes after a
given amount of local time has elapsed.
Suppose the initial offset relative to the source is U (fast +ve,
slow -ve) and a time interval T elapses measured in terms of the
local clock. Then the error relative to the source at the end of
the interval should lie in the interval [U+T*lo, U+T*hi]. */
void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
SST_Stats
SRC_GetSourcestats(SRC_Instance instance)
{
assert(initialised);
SST_GetFrequencyRange(instance->stats, lo, hi);
return instance->stats;
}
/* ================================================== */
@@ -342,7 +334,7 @@ void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
void SRC_AccumulateSample
(SRC_Instance inst,
struct timeval *sample_time,
struct timespec *sample_time,
double offset,
double peer_delay,
double peer_dispersion,
@@ -356,11 +348,12 @@ void SRC_AccumulateSample
inst->leap_status = leap_status;
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
root_delay, root_dispersion, stratum);
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
LOG(LOGS_INFO, "Dropping sample around leap second");
return;
}
@@ -404,7 +397,7 @@ special_mode_end(void)
/* Check if the source could still have enough samples to be selectable */
if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size +
SRC_Samples(sources[i]) >= MIN_SAMPLES_FOR_REGRESS)
SST_Samples(sources[i]->stats) >= MIN_SAMPLES_FOR_REGRESS)
return 0;
}
@@ -432,10 +425,11 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
}
/* Try to replace NTP sources that are unreachable, falsetickers, or
have root distance larger than the allowed maximum */
have root distance or jitter larger than the allowed maximums */
if (inst->type == SRC_NTP &&
((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) ||
inst->status == SRC_FALSETICKER || inst->status == SRC_BAD_DISTANCE)) {
inst->status == SRC_BAD_DISTANCE || inst->status == SRC_JITTERY ||
inst->status == SRC_FALSETICKER)) {
NSR_HandleBadSource(inst->ip_addr);
}
}
@@ -457,7 +451,7 @@ log_selection_message(char *format, char *arg)
{
if (REF_GetMode() != REF_ModeNormal)
return;
LOG(LOGS_INFO, LOGF_Sources, format, arg);
LOG(LOGS_INFO, format, arg);
}
/* ================================================== */
@@ -514,10 +508,10 @@ mark_ok_sources(SRC_Status status)
/* ================================================== */
static int
combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
double *offset_sd, double *frequency, double *skew)
{
struct timeval src_ref_time;
struct timespec src_ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
@@ -564,12 +558,12 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
if (sources[index]->status == SRC_OK)
sources[index]->status = SRC_UNSELECTED;
UTI_DiffTimevalsToDouble(&elapsed, ref_time, &src_ref_time);
elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency;
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / src_skew;
DEBUG_LOG(LOGF_Sources, "combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
DEBUG_LOG("combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
sum_offset_weight += offset_weight;
@@ -590,7 +584,7 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
*frequency = sum_frequency / sum_frequency_weight;
*skew = 1.0 / sqrt(inv_sum2_skew);
DEBUG_LOG(LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e",
DEBUG_LOG("combined result offset=%e sd=%e freq=%e skew=%e",
*offset, *offset_sd, *frequency, *skew);
return combined;
@@ -604,12 +598,12 @@ void
SRC_SelectSource(SRC_Instance updated_inst)
{
struct SelectInfo *si;
struct timeval now, ref_time;
struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources;
int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
int depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source;
int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score;
@@ -657,7 +651,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
&si->lo_limit, &si->hi_limit, &si->root_distance,
&si->variance, &first_sample_ago,
&si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok);
if (!si->select_ok) {
@@ -674,6 +668,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* And the same applies for the estimated standard deviation */
if (si->std_dev > max_jitter) {
sources[i]->status = SRC_JITTERY;
continue;
}
sources[i]->status = SRC_OK; /* For now */
if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago)
@@ -736,12 +736,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
uint32_t local_ref_id = NSR_GetLocalRefid(sources[orphan_source]->ip_addr);
if (!local_ref_id) {
LOG(LOGS_ERR, LOGF_Sources, "Unknown local refid in orphan mode");
LOG(LOGS_ERR, "Unknown local refid in orphan mode");
} else if (sources[orphan_source]->ref_id < local_ref_id) {
sources[orphan_source]->status = SRC_OK;
n_sel_sources = 1;
DEBUG_LOG(LOGF_Sources, "selecting orphan refid=%"PRIx32,
sources[orphan_source]->ref_id);
DEBUG_LOG("selecting orphan refid=%"PRIx32, sources[orphan_source]->ref_id);
}
}
@@ -765,7 +764,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints += 2;
}
DEBUG_LOG(LOGF_Sources, "badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f",
DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f",
n_badstats_sources, n_sel_sources, max_badstat_reach,
max_sel_reach, max_reach_sample_ago);
@@ -910,18 +909,22 @@ SRC_SelectSource(SRC_Instance updated_inst)
return;
}
/* Accept leap second status if more than half of selectable sources agree */
for (i = j1 = j2 = 0; i < n_sel_sources; i++) {
/* Accept leap second status if more than half of selectable (and trusted
if there are any) sources agree */
for (i = leap_ins = leap_del = leap_votes = 0; i < n_sel_sources; i++) {
index = sel_sources[i];
if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
continue;
leap_votes++;
if (sources[index]->leap_status == LEAP_InsertSecond)
j1++;
leap_ins++;
else if (sources[index]->leap_status == LEAP_DeleteSecond)
j2++;
leap_del++;
}
if (j1 > n_sel_sources / 2)
if (leap_ins > leap_votes / 2)
leap_status = LEAP_InsertSecond;
else if (j2 > n_sel_sources / 2)
else if (leap_del > leap_votes / 2)
leap_status = LEAP_DeleteSecond;
else
leap_status = LEAP_Normal;
@@ -999,7 +1002,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
sources[i]->sel_score = 1.0 / distance;
}
DEBUG_LOG(LOGF_Sources, "select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f",
DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f",
sources[i]->sel_score, sources[i]->ref_id,
updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance);
@@ -1023,7 +1026,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[max_score_index]->updates == 0) {
selected_source_index = INVALID_SOURCE;
mark_ok_sources(SRC_WAITS_UPDATE);
DEBUG_LOG(LOGF_Sources, "best source has no updates");
DEBUG_LOG("best source has no updates");
return;
}
@@ -1094,36 +1097,10 @@ SRC_SetReselectDistance(double distance)
{
if (reselect_distance != distance) {
reselect_distance = distance;
LOG(LOGS_INFO, LOGF_Sources, "New reselect distance %f", distance);
LOG(LOGS_INFO, "New reselect distance %f", distance);
}
}
/* ================================================== */
double
SRC_PredictOffset(SRC_Instance inst, struct timeval *when)
{
return SST_PredictOffset(inst->stats, when);
}
/* ================================================== */
double
SRC_MinRoundTripDelay(SRC_Instance inst)
{
return SST_MinRoundTripDelay(inst->stats);
}
/* ================================================== */
int
SRC_IsGoodSample(SRC_Instance inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timeval *when)
{
return SST_IsGoodSample(inst->stats, offset, delay, max_delay_dev_ratio,
clock_error, when);
}
/* ================================================== */
/* This routine is registered as a callback with the local clock
module, to be called whenever the local clock changes frequency or
@@ -1132,12 +1109,8 @@ SRC_IsGoodSample(SRC_Instance inst, double offset, double delay,
the new regime. */
static void
slew_sources(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
LCL_ChangeType change_type,
void *anything)
slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
int i;
@@ -1169,6 +1142,39 @@ add_dispersion(double dispersion, void *anything)
}
}
/* ================================================== */
static
FILE *open_dumpfile(SRC_Instance inst, const char *mode)
{
FILE *f;
char filename[1024], *dumpdir;
dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0') {
LOG(LOGS_WARN, "dumpdir not specified");
return NULL;
}
/* Include IP address in the name for NTP sources, or reference ID in hex */
if ((inst->type == SRC_NTP &&
snprintf(filename, sizeof (filename), "%s/%s.dat", dumpdir,
source_to_string(inst)) >= sizeof (filename)) ||
(inst->type != SRC_NTP &&
snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat",
dumpdir, inst->ref_id) >= sizeof (filename))) {
LOG(LOGS_WARN, "dumpdir too long");
return NULL;
}
f = fopen(filename, mode);
if (!f && mode[0] != 'r')
LOG(LOGS_WARN, "Could not open dump file for %s",
source_to_string(inst));
return f;
}
/* ================================================== */
/* This is called to dump out the source measurement registers */
@@ -1176,34 +1182,15 @@ void
SRC_DumpSources(void)
{
FILE *out;
int direc_len, file_len;
char *filename;
unsigned int a, b, c, d;
int i;
char *direc;
direc = CNF_GetDumpDir();
direc_len = strlen(direc);
file_len = direc_len + 24;
filename = MallocArray(char, file_len); /* a bit of slack */
for (i = 0; i < n_sources; i++) {
a = (sources[i]->ref_id) >> 24;
b = ((sources[i]->ref_id) >> 16) & 0xff;
c = ((sources[i]->ref_id) >> 8) & 0xff;
d = ((sources[i]->ref_id)) & 0xff;
snprintf(filename, file_len - 1, "%s/%d.%d.%d.%d.dat", direc, a, b, c, d);
out = fopen(filename, "w");
if (!out) {
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
} else {
SST_SaveToFile(sources[i]->stats, out);
fclose(out);
}
out = open_dumpfile(sources[i], "w");
if (!out)
continue;
SST_SaveToFile(sources[i]->stats, out);
fclose(out);
}
Free(filename);
}
/* ================================================== */
@@ -1212,40 +1199,63 @@ void
SRC_ReloadSources(void)
{
FILE *in;
char *filename;
unsigned int a, b, c, d;
int i;
char *dumpdir;
int dumpdirlen, filelen;
for (i=0; i<n_sources; i++) {
a = (sources[i]->ref_id) >> 24;
b = ((sources[i]->ref_id) >> 16) & 0xff;
c = ((sources[i]->ref_id) >> 8) & 0xff;
d = ((sources[i]->ref_id)) & 0xff;
dumpdir = CNF_GetDumpDir();
dumpdirlen = strlen(dumpdir);
filelen = dumpdirlen + 24;
filename = MallocArray(char, filelen);
snprintf(filename, filelen-1, "%s/%d.%d.%d.%d.dat", dumpdir, a, b, c, d);
in = fopen(filename, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
} else {
if (SST_LoadFromFile(sources[i]->stats, in)) {
SST_DoNewRegression(sources[i]->stats);
} else {
LOG(LOGS_WARN, LOGF_Sources, "Problem loading from file %s", filename);
}
fclose(in);
}
Free(filename);
for (i = 0; i < n_sources; i++) {
in = open_dumpfile(sources[i], "r");
if (!in)
continue;
if (!SST_LoadFromFile(sources[i]->stats, in))
LOG(LOGS_WARN, "Could not load dump file for %s",
source_to_string(sources[i]));
else
LOG(LOGS_INFO, "Loaded dump file for %s",
source_to_string(sources[i]));
fclose(in);
}
}
/* ================================================== */
void
SRC_RemoveDumpFiles(void)
{
char pattern[1024], name[64], *dumpdir, *s;
IPAddr ip_addr;
glob_t gl;
size_t i;
dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0' ||
snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern))
return;
if (glob(pattern, 0, NULL, &gl))
return;
for (i = 0; i < gl.gl_pathc; i++) {
s = strrchr(gl.gl_pathv[i], '/');
if (!s || snprintf(name, sizeof (name), "%s", s + 1) >= sizeof (name))
continue;
/* Remove .dat extension */
if (strlen(name) < 4)
continue;
name[strlen(name) - 4] = '\0';
/* Check if it looks like name of an actual dump file */
if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
continue;
DEBUG_LOG("Removing %s", gl.gl_pathv[i]);
unlink(gl.gl_pathv[i]);
}
globfree(&gl);
}
/* ================================================== */
int
SRC_IsSyncPeer(SRC_Instance inst)
{
@@ -1290,7 +1300,7 @@ SRC_ActiveSources(void)
/* ================================================== */
int
SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
{
SRC_Instance src;
if ((index >= n_sources) || (index < 0)) {
@@ -1298,7 +1308,6 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
} else {
src = sources[index];
memset(&report->ip_addr, 0, sizeof (report->ip_addr));
if (src->ip_addr)
report->ip_addr = *src->ip_addr;
else {
@@ -1308,14 +1317,6 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
}
switch (src->status) {
case SRC_UNSELECTABLE:
case SRC_BAD_STATS:
case SRC_BAD_DISTANCE:
case SRC_STALE:
case SRC_ORPHAN:
case SRC_WAITS_STATS:
report->state = RPT_UNREACH;
break;
case SRC_FALSETICKER:
report->state = RPT_FALSETICKER;
break;
@@ -1336,9 +1337,8 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
case SRC_SELECTED:
report->state = RPT_SYNC;
break;
case SRC_OK:
default:
assert(0);
report->state = RPT_UNREACH;
break;
}
@@ -1356,7 +1356,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
/* ================================================== */
int
SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now)
SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now)
{
SRC_Instance src;
@@ -1385,11 +1385,3 @@ SRC_GetType(int index)
}
/* ================================================== */
int
SRC_Samples(SRC_Instance inst)
{
return SST_Samples(inst->stats);
}
/* ================================================== */

View File

@@ -34,6 +34,7 @@
#include "ntp.h"
#include "reports.h"
#include "sourcestats.h"
/* Size of the source reachability register */
#define SOURCE_REACH_BITS 8
@@ -73,18 +74,8 @@ extern void SRC_ResetInstance(SRC_Instance instance);
/* Function to change the sources's reference ID and IP address */
extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
/* Function to get the range of frequencies, relative to the given
source, that we believe the local clock lies within. The return
values are in terms of the number of seconds fast (+ve) or slow
(-ve) relative to the source that the local clock becomes after a
given amount of local time has elapsed.
Suppose the initial offset relative to the source is U (fast +ve,
slow -ve) and a time interval T elapses measured in terms of the
local clock. Then the error relative to the source at the end of
the interval should lie in the interval [U+T*lo, U+T*hi]. */
extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi);
/* Function to get access to the sourcestats instance */
extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
/* This function is called by one of the source drivers when it has
a new sample that is to be accumulated.
@@ -114,7 +105,7 @@ extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
*/
extern void SRC_AccumulateSample(SRC_Instance instance, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
extern void SRC_AccumulateSample(SRC_Instance instance, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
/* This routine sets the source as receiving reachability updates */
extern void SRC_SetActive(SRC_Instance inst);
@@ -143,34 +134,18 @@ extern void SRC_ReselectSource(void);
/* Set reselect distance */
extern void SRC_SetReselectDistance(double distance);
/* Predict the offset of the local clock relative to a given source at
a given local cooked time. Positive indicates local clock is FAST
relative to reference. */
extern double SRC_PredictOffset(SRC_Instance inst, struct timeval *when);
/* Return the minimum peer delay amongst the previous samples
currently held in the register */
extern double SRC_MinRoundTripDelay(SRC_Instance inst);
/* This routine determines if a new sample is good enough that it should be
accumulated */
extern int SRC_IsGoodSample(SRC_Instance inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when);
extern void SRC_DumpSources(void);
extern void SRC_ReloadSources(void);
extern void SRC_RemoveDumpFiles(void);
extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
extern SRC_Type SRC_GetType(int index);
extern int SRC_Samples(SRC_Instance inst);
#endif /* GOT_SOURCES_H */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014
* Copyright (C) Miroslav Lichvar 2011-2014, 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -47,8 +47,22 @@
2000ppm, which would be pretty bad */
#define WORST_CASE_FREQ_BOUND (2000.0/1.0e6)
/* The minimum allowed skew */
/* The minimum and maximum assumed skew */
#define MIN_SKEW 1.0e-12
#define MAX_SKEW 1.0e+02
/* The asymmetry of network jitter when all jitter is in one direction */
#define MAX_ASYMMETRY 0.5
/* The minimum estimated asymmetry that can activate the offset correction */
#define MIN_ASYMMETRY 0.45
/* The minimum number of consecutive asymmetries with the same sign needed
to activate the offset correction */
#define MIN_ASYMMETRY_RUN 10
/* The maximum value of the counter */
#define MAX_ASYMMETRY_RUN 1000
/* ================================================== */
@@ -72,8 +86,8 @@ struct SST_Stats_Record {
buffer. */
int n_samples;
/* Number of extra samples stored in sample_times and offsets arrays that are
used to extend runs test */
/* Number of extra samples stored in sample_times, offsets and peer_delays
arrays that are used to extend the runs test */
int runs_samples;
/* The index of the newest sample */
@@ -92,11 +106,18 @@ struct SST_Stats_Record {
/* This is the estimated offset (+ve => local fast) at a particular time */
double estimated_offset;
double estimated_offset_sd;
struct timeval offset_time;
struct timespec offset_time;
/* Number of runs of the same sign amongst the residuals */
int nruns;
/* Number of consecutive estimated asymmetries with the same sign.
The sign of the number encodes the sign of the asymmetry. */
int asymmetry_run;
/* This is the latest estimated asymmetry of network jitter */
double asymmetry;
/* This value contains the estimated frequency. This is the number
of seconds that the local clock gains relative to the reference
source per unit local time. (Positive => local clock fast,
@@ -108,12 +129,12 @@ struct SST_Stats_Record {
about estimated_frequency */
double skew;
/* This is the estimated residual variance of the data points */
double variance;
/* This is the estimated standard deviation of the data points */
double std_dev;
/* This array contains the sample epochs, in terms of the local
clock. */
struct timeval sample_times[MAX_SAMPLES * REGRESS_RUNS_RATIO];
struct timespec sample_times[MAX_SAMPLES * REGRESS_RUNS_RATIO];
/* This is an array of offsets, in seconds, corresponding to the
sample times. In this module, we use the convention that
@@ -131,7 +152,7 @@ struct SST_Stats_Record {
/* This is an array of peer delays, in seconds, being the roundtrip
measurement delay to the peer */
double peer_delays[MAX_SAMPLES];
double peer_delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
/* This is an array of peer dispersions, being the skew and local
precision dispersion terms from sampling the peer */
@@ -161,7 +182,7 @@ void
SST_Initialise(void)
{
logfileid = CNF_GetLogStatistics() ? LOG_FileOpen("statistics",
" Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr")
" Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr Asym")
: -1;
}
@@ -214,10 +235,11 @@ SST_ResetInstance(SST_Stats inst)
inst->skew = 2000.0e-6;
inst->estimated_offset = 0.0;
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
inst->offset_time.tv_sec = 0;
inst->offset_time.tv_usec = 0;
inst->variance = 16.0;
UTI_ZeroTimespec(&inst->offset_time);
inst->std_dev = 4.0;
inst->nruns = 0;
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
}
/* ================================================== */
@@ -253,7 +275,7 @@ prune_register(SST_Stats inst, int new_oldest)
/* ================================================== */
void
SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
double offset,
double peer_delay, double peer_dispersion,
double root_delay, double root_dispersion,
@@ -269,8 +291,8 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
/* Make sure it's newer than the last sample */
if (inst->n_samples &&
UTI_CompareTimevals(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s",
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
LOG(LOGS_WARN, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
SST_ResetInstance(inst);
}
@@ -282,14 +304,14 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
inst->sample_times[n] = *sample_time;
inst->offsets[n] = offset;
inst->orig_offsets[m] = offset;
inst->peer_delays[m] = peer_delay;
inst->peer_delays[n] = peer_delay;
inst->peer_dispersions[m] = peer_dispersion;
inst->root_delays[m] = root_delay;
inst->root_dispersions[m] = root_dispersion;
inst->strata[m] = stratum;
if (!inst->n_samples || inst->peer_delays[m] < inst->peer_delays[inst->min_delay_sample])
inst->min_delay_sample = m;
if (!inst->n_samples || inst->peer_delays[n] < inst->peer_delays[inst->min_delay_sample])
inst->min_delay_sample = n;
++inst->n_samples;
}
@@ -323,14 +345,13 @@ get_buf_index(SST_Stats inst, int i)
static void
convert_to_intervals(SST_Stats inst, double *times_back)
{
struct timeval *newest_tv;
struct timespec *ts;
int i;
newest_tv = &(inst->sample_times[inst->last_sample]);
ts = &inst->sample_times[inst->last_sample];
for (i = -inst->runs_samples; i < inst->n_samples; i++) {
/* The entries in times_back[] should end up negative */
UTI_DiffTimevalsToDouble(&times_back[i],
&inst->sample_times[get_runsbuf_index(inst, i)], newest_tv);
times_back[i] = UTI_DiffTimespecsToDouble(&inst->sample_times[get_runsbuf_index(inst, i)], ts);
}
}
@@ -376,15 +397,65 @@ find_min_delay_sample(SST_Stats inst)
{
int i, index;
inst->min_delay_sample = get_buf_index(inst, 0);
inst->min_delay_sample = get_runsbuf_index(inst, -inst->runs_samples);
for (i = 1; i < inst->n_samples; i++) {
index = get_buf_index(inst, i);
for (i = -inst->runs_samples + 1; i < inst->n_samples; i++) {
index = get_runsbuf_index(inst, i);
if (inst->peer_delays[index] < inst->peer_delays[inst->min_delay_sample])
inst->min_delay_sample = index;
}
}
/* ================================================== */
/* This function estimates asymmetry of network jitter on the path to the
source as a slope of offset against network delay in multiple linear
regression. If the asymmetry is significant and its sign doesn't change
frequently, the measured offsets (which are used later to estimate the
offset and frequency of the clock) are corrected to correspond to the
minimum network delay. This can significantly improve the accuracy and
stability of the estimated offset and frequency. */
static void
correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
{
double asymmetry, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
int i, n;
/* Don't try to estimate the asymmetry with reference clocks */
if (!inst->ip_addr)
return;
n = inst->runs_samples + inst->n_samples;
for (i = 0; i < n; i++)
delays[i] = inst->peer_delays[get_runsbuf_index(inst, i - inst->runs_samples)] -
inst->peer_delays[inst->min_delay_sample];
/* Reset the counter when the regression fails or the sign changes */
if (!RGR_MultipleRegress(times_back, delays, offsets, n, &asymmetry) ||
asymmetry * inst->asymmetry_run < 0.0) {
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
return;
}
asymmetry = CLAMP(-MAX_ASYMMETRY, asymmetry, MAX_ASYMMETRY);
if (asymmetry <= -MIN_ASYMMETRY && inst->asymmetry_run > -MAX_ASYMMETRY_RUN)
inst->asymmetry_run--;
else if (asymmetry >= MIN_ASYMMETRY && inst->asymmetry_run < MAX_ASYMMETRY_RUN)
inst->asymmetry_run++;
if (abs(inst->asymmetry_run) < MIN_ASYMMETRY_RUN)
return;
/* Correct the offsets */
for (i = 0; i < n; i++)
offsets[i] -= asymmetry * delays[i];
inst->asymmetry = asymmetry;
}
/* ================================================== */
/* This defines the assumed ratio between the standard deviation of
@@ -392,7 +463,7 @@ find_min_delay_sample(SST_Stats inst)
time. E.g. a value of 4 means that we think the standard deviation
is four times the fluctuation of the peer distance */
#define SD_TO_DIST_RATIO 1.0
#define SD_TO_DIST_RATIO 0.7
/* ================================================== */
/* This function runs the linear regression operation on the data. It
@@ -412,9 +483,10 @@ SST_DoNewRegression(SST_Stats inst)
int best_start, times_back_start;
double est_intercept, est_slope, est_var, est_intercept_sd, est_slope_sd;
int i, j, nruns;
double min_distance, mean_distance;
double min_distance, median_distance;
double sd_weight, sd;
double old_skew, old_freq, stress;
double precision;
convert_to_intervals(inst, times_back + inst->runs_samples);
@@ -423,28 +495,34 @@ SST_DoNewRegression(SST_Stats inst)
offsets[i + inst->runs_samples] = inst->offsets[get_runsbuf_index(inst, i)];
}
for (i = 0, mean_distance = 0.0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
for (i = 0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
j = get_buf_index(inst, i);
peer_distances[i] = 0.5 * inst->peer_delays[j] + inst->peer_dispersions[j];
mean_distance += peer_distances[i];
peer_distances[i] = 0.5 * inst->peer_delays[get_runsbuf_index(inst, i)] +
inst->peer_dispersions[j];
if (peer_distances[i] < min_distance) {
min_distance = peer_distances[i];
}
}
mean_distance /= inst->n_samples;
/* And now, work out the weight vector */
sd = mean_distance - min_distance;
if (sd > min_distance || sd <= 0.0)
sd = min_distance;
precision = LCL_GetSysPrecisionAsQuantum();
median_distance = RGR_FindMedian(peer_distances, inst->n_samples);
sd = (median_distance - min_distance) / SD_TO_DIST_RATIO;
sd = CLAMP(precision, sd, min_distance);
min_distance += precision;
for (i=0; i<inst->n_samples; i++) {
sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / sd;
sd_weight = 1.0;
if (peer_distances[i] > min_distance)
sd_weight += (peer_distances[i] - min_distance) / sd;
weights[i] = sd_weight * sd_weight;
}
}
correct_asymmetry(inst, times_back, offsets);
inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples,
offsets + inst->runs_samples, weights,
inst->n_samples, inst->runs_samples,
@@ -463,26 +541,26 @@ SST_DoNewRegression(SST_Stats inst)
inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample];
inst->estimated_offset_sd = est_intercept_sd;
inst->variance = est_var;
inst->std_dev = sqrt(est_var);
inst->nruns = nruns;
if (inst->skew < MIN_SKEW)
inst->skew = MIN_SKEW;
inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
DEBUG_LOG("off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
inst->estimated_offset, inst->estimated_frequency, inst->skew,
inst->n_samples, best_start, inst->nruns,
inst->asymmetry, inst->asymmetry_run);
if (logfileid != -1) {
LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d",
LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d %5.2f",
UTI_TimeToLogForm(inst->offset_time.tv_sec),
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid),
sqrt(inst->variance),
inst->estimated_offset,
inst->estimated_offset_sd,
inst->estimated_frequency,
inst->skew,
stress,
inst->n_samples,
best_start, nruns);
inst->std_dev,
inst->estimated_offset, inst->estimated_offset_sd,
inst->estimated_frequency, inst->skew, stress,
inst->n_samples, best_start, inst->nruns,
inst->asymmetry);
}
times_back_start = inst->runs_samples + best_start;
@@ -524,12 +602,12 @@ SST_GetFrequencyRange(SST_Stats inst,
/* ================================================== */
void
SST_GetSelectionData(SST_Stats inst, struct timeval *now,
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
double *variance,
double *std_dev,
double *first_sample_ago,
double *last_sample_ago,
int *select_ok)
@@ -546,9 +624,9 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
j = get_buf_index(inst, inst->best_single_sample);
*stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
*variance = inst->variance;
*std_dev = inst->std_dev;
UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[i]);
sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency;
*root_distance = 0.5 * inst->root_delays[j] +
inst->root_dispersions[j] + sample_elapsed * inst->skew;
@@ -560,10 +638,10 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
double average_offset, elapsed;
int average_ok;
/* average_ok ignored for now */
UTI_DiffTimevalsToDouble(&elapsed, now, &(inst->offset_time));
elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed;
if (fabs(average_offset - offset) <=
inst->peer_dispersions[j] + 0.5 * inst->peer_delays[j]) {
inst->peer_dispersions[j] + 0.5 * inst->peer_delays[i]) {
average_ok = 1;
} else {
average_ok = 0;
@@ -571,21 +649,21 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
#endif
i = get_runsbuf_index(inst, 0);
UTI_DiffTimevalsToDouble(first_sample_ago, now, &inst->sample_times[i]);
*first_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
i = get_runsbuf_index(inst, inst->n_samples - 1);
UTI_DiffTimevalsToDouble(last_sample_ago, now, &inst->sample_times[i]);
*last_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
*select_ok = inst->regression_ok;
DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f var=%f first_ago=%f last_ago=%f selok=%d",
inst->n_samples, offset, *root_distance, *variance,
DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
inst->n_samples, offset, *root_distance, *std_dev,
*first_sample_ago, *last_sample_ago, *select_ok);
}
/* ================================================== */
void
SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *root_delay, double *root_dispersion)
@@ -605,22 +683,23 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
*skew = inst->skew;
*root_delay = inst->root_delays[j];
UTI_DiffTimevalsToDouble(&elapsed_sample, &inst->offset_time, &inst->sample_times[i]);
elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew, *average_offset, *offset_sd, *root_dispersion);
DEBUG_LOG("n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew,
*average_offset, *offset_sd, *root_dispersion);
}
/* ================================================== */
void
SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset)
SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset)
{
int m, i;
double delta_time;
struct timeval *sample, prev;
struct timespec *sample, prev;
double prev_offset, prev_freq;
if (!inst->n_samples)
@@ -628,9 +707,9 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
for (m = -inst->runs_samples; m < inst->n_samples; m++) {
i = get_runsbuf_index(inst, m);
sample = &(inst->sample_times[i]);
sample = &inst->sample_times[i];
prev = *sample;
UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
inst->offsets[i] += delta_time;
}
@@ -638,14 +717,14 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
prev = inst->offset_time;
prev_offset = inst->estimated_offset;
prev_freq = inst->estimated_frequency;
UTI_AdjustTimeval(&(inst->offset_time), when, &(inst->offset_time),
UTI_AdjustTimespec(&inst->offset_time, when, &inst->offset_time,
&delta_time, dfreq, doffset);
inst->estimated_offset += delta_time;
inst->estimated_frequency = (inst->estimated_frequency - dfreq) / (1.0 - dfreq);
DEBUG_LOG(LOGF_SourceStats, "n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f",
DEBUG_LOG("n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f",
inst->n_samples, inst->runs_samples,
UTI_TimevalToString(&prev), UTI_TimevalToString(&(inst->offset_time)),
UTI_TimespecToString(&prev), UTI_TimespecToString(&inst->offset_time),
prev_offset, inst->estimated_offset,
1.0e6 * prev_freq, 1.0e6 * inst->estimated_frequency);
}
@@ -667,7 +746,7 @@ SST_AddDispersion(SST_Stats inst, double dispersion)
/* ================================================== */
double
SST_PredictOffset(SST_Stats inst, struct timeval *when)
SST_PredictOffset(SST_Stats inst, struct timespec *when)
{
double elapsed;
@@ -681,7 +760,7 @@ SST_PredictOffset(SST_Stats inst, struct timeval *when)
return 0.0;
}
} else {
UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time);
elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
return inst->estimated_offset + elapsed * inst->estimated_frequency;
}
@@ -701,20 +780,20 @@ SST_MinRoundTripDelay(SST_Stats inst)
int
SST_IsGoodSample(SST_Stats inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timeval *when)
double max_delay_dev_ratio, double clock_error, struct timespec *when)
{
double elapsed, allowed_increase, delay_increase;
if (inst->n_samples < 3)
if (inst->n_samples < 6)
return 1;
UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time);
elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
/* Require that the ratio of the increase in delay from the minimum to the
standard deviation is less than max_delay_dev_ratio. In the allowed
increase in delay include also skew and clock_error. */
allowed_increase = sqrt(inst->variance) * max_delay_dev_ratio +
allowed_increase = inst->std_dev * max_delay_dev_ratio +
elapsed * (inst->skew + clock_error);
delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0;
@@ -729,8 +808,8 @@ SST_IsGoodSample(SST_Stats inst, double offset, double delay,
if (fabs(offset) - delay_increase > allowed_increase)
return 1;
DEBUG_LOG(LOGF_SourceStats, "Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f",
offset, delay, allowed_increase, delay_increase);
DEBUG_LOG("Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f",
offset, delay, delay_increase, allowed_increase);
return 0;
}
@@ -750,12 +829,18 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
i = get_runsbuf_index(inst, m);
j = get_buf_index(inst, m);
fprintf(out, "%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
(unsigned long) inst->sample_times[i].tv_sec,
(unsigned long) inst->sample_times[i].tv_usec,
fprintf(out,
#ifdef HAVE_LONG_TIME_T
"%08"PRIx64" %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
(uint64_t)inst->sample_times[i].tv_sec,
#else
"%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
(unsigned long)inst->sample_times[i].tv_sec,
#endif
(unsigned long)inst->sample_times[i].tv_nsec / 1000,
inst->offsets[i],
inst->orig_offsets[j],
inst->peer_delays[j],
inst->peer_delays[i],
inst->peer_dispersions[j],
inst->root_delays[j],
inst->root_dispersions[j],
@@ -763,6 +848,8 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
inst->strata[j]);
}
fprintf(out, "%d\n", inst->asymmetry_run);
}
/* ================================================== */
@@ -771,22 +858,30 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
int
SST_LoadFromFile(SST_Stats inst, FILE *in)
{
int i, line_number;
#ifdef HAVE_LONG_TIME_T
uint64_t sec;
#else
unsigned long sec;
#endif
unsigned long usec;
int i;
char line[1024];
unsigned long sec, usec;
double weight;
assert(!inst->n_samples);
if (fgets(line, sizeof(line), in) &&
sscanf(line, "%d", &inst->n_samples) == 1 &&
inst->n_samples > 0 && inst->n_samples <= MAX_SAMPLES) {
line_number = 2;
inst->n_samples >= 0 && inst->n_samples <= MAX_SAMPLES) {
for (i=0; i<inst->n_samples; i++) {
if (!fgets(line, sizeof(line), in) ||
(sscanf(line, "%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
(sscanf(line,
#ifdef HAVE_LONG_TIME_T
"%"SCNx64"%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
#else
"%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
#endif
&(sec), &(usec),
&(inst->offsets[i]),
&(inst->orig_offsets[i]),
@@ -799,40 +894,44 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
/* This is the branch taken if the read FAILED */
LOG(LOGS_WARN, LOGF_SourceStats, "Failed to read data from line %d of dump file", line_number);
inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0;
} else {
/* This is the branch taken if the read is SUCCESSFUL */
inst->sample_times[i].tv_sec = sec;
inst->sample_times[i].tv_usec = usec;
line_number++;
inst->sample_times[i].tv_nsec = 1000 * usec;
UTI_NormaliseTimespec(&inst->sample_times[i]);
}
}
/* This field was not saved in older versions */
if (!fgets(line, sizeof(line), in) || sscanf(line, "%d\n", &inst->asymmetry_run) != 1)
inst->asymmetry_run = 0;
} else {
LOG(LOGS_WARN, LOGF_SourceStats, "Could not read number of samples from dump file");
inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0;
}
if (!inst->n_samples)
return 1;
inst->last_sample = inst->n_samples - 1;
inst->runs_samples = 0;
find_min_delay_sample(inst);
SST_DoNewRegression(inst);
return 1;
}
/* ================================================== */
void
SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now)
SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now)
{
int i, j;
struct timeval ago;
struct timespec last_sample_time;
if (inst->n_samples > 0) {
i = get_runsbuf_index(inst, inst->n_samples - 1);
@@ -842,8 +941,10 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j];
UTI_DiffTimevals(&ago, now, &inst->sample_times[i]);
report->latest_meas_ago = ago.tv_sec;
/* Align the sample time to reduce the leak of the receive timestamp */
last_sample_time = inst->sample_times[i];
last_sample_time.tv_nsec = 0;
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else {
report->latest_meas_ago = (uint32_t)-1;
report->orig_latest_meas = 0;
@@ -864,7 +965,7 @@ SST_Samples(SST_Stats inst)
/* ================================================== */
void
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timeval *now)
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
{
double dspan;
double elapsed, sample_elapsed;
@@ -876,15 +977,15 @@ SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct ti
if (inst->n_samples > 1) {
li = get_runsbuf_index(inst, inst->n_samples - 1);
lj = get_buf_index(inst, inst->n_samples - 1);
UTI_DiffTimevalsToDouble(&dspan, &inst->sample_times[li],
dspan = UTI_DiffTimespecsToDouble(&inst->sample_times[li],
&inst->sample_times[get_runsbuf_index(inst, 0)]);
report->span_seconds = (unsigned long) (dspan + 0.5);
if (inst->n_samples > 3) {
UTI_DiffTimevalsToDouble(&elapsed, now, &inst->offset_time);
elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
bi = get_runsbuf_index(inst, inst->best_single_sample);
bj = get_buf_index(inst, inst->best_single_sample);
UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[bi]);
sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[bi]);
report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency;
report->est_offset_err = (inst->estimated_offset_sd +
sample_elapsed * inst->skew +
@@ -901,7 +1002,15 @@ SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct ti
report->resid_freq_ppm = 1.0e6 * inst->estimated_frequency;
report->skew_ppm = 1.0e6 * inst->skew;
report->sd = sqrt(inst->variance);
report->sd = inst->std_dev;
}
/* ================================================== */
double
SST_GetJitterAsymmetry(SST_Stats inst)
{
return inst->asymmetry;
}
/* ================================================== */

View File

@@ -61,7 +61,7 @@ extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
stratum is the stratum of the source from which the sample came.
*/
extern void SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
extern void SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
/* This function runs the linear regression operation on the data. It
finds the set of most recent samples that give the tightest
@@ -77,7 +77,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */
extern void
SST_GetSelectionData(SST_Stats inst, struct timeval *now,
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit,
double *offset_hi_limit,
@@ -89,7 +89,7 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
/* Get data needed when setting up tracking on this source */
extern void
SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
double *frequency, double *skew,
double *root_delay, double *root_dispersion);
@@ -110,7 +110,7 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
*/
extern void SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset);
extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset);
/* This routine is called when an indeterminate offset is introduced
into the local time. */
@@ -119,7 +119,7 @@ extern void SST_AddDispersion(SST_Stats inst, double dispersion);
/* Predict the offset of the local clock relative to a given source at
a given local cooked time. Positive indicates local clock is FAST
relative to reference. */
extern double SST_PredictOffset(SST_Stats inst, struct timeval *when);
extern double SST_PredictOffset(SST_Stats inst, struct timespec *when);
/* Find the minimum round trip delay in the register */
extern double SST_MinRoundTripDelay(SST_Stats inst);
@@ -127,17 +127,19 @@ extern double SST_MinRoundTripDelay(SST_Stats inst);
/* This routine determines if a new sample is good enough that it should be
accumulated */
extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay,
double max_delay_dev_ratio, double clock_error, struct timeval *when);
double max_delay_dev_ratio, double clock_error, struct timespec *when);
extern void SST_SaveToFile(SST_Stats inst, FILE *out);
extern int SST_LoadFromFile(SST_Stats inst, FILE *in);
extern void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now);
extern void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now);
extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timeval *now);
extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now);
extern int SST_Samples(SST_Stats inst);
extern double SST_GetJitterAsymmetry(SST_Stats inst);
#endif /* GOT_SOURCESTATS_H */

View File

@@ -42,22 +42,24 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
int interleaved;
int sel_options;
uint32_t authkey;
double max_delay;
double max_delay_ratio;
double max_delay_dev_ratio;
double offset;
} SourceParameters;
#define SRC_DEFAULT_PORT 123
#define SRC_DEFAULT_MINPOLL 6
#define SRC_DEFAULT_MAXPOLL 10
#define SRC_DEFAULT_PRESEND_MINPOLL 0
#define SRC_DEFAULT_PRESEND_MINPOLL 100
#define SRC_DEFAULT_MAXDELAY 3.0
#define SRC_DEFAULT_MAXDELAYRATIO 0.0
#define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0
#define SRC_DEFAULT_MINSTRATUM 0
#define SRC_DEFAULT_POLLTARGET 6
#define SRC_DEFAULT_POLLTARGET 8
#define SRC_DEFAULT_MAXSOURCES 4
#define SRC_DEFAULT_MINSAMPLES (-1)
#define SRC_DEFAULT_MAXSAMPLES (-1)

59
stubs.c
View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014-2015
* Copyright (C) Miroslav Lichvar 2014-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -38,9 +38,11 @@
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_sources.h"
#include "ntp_signd.h"
#include "privops.h"
#include "refclock.h"
#include "sched.h"
#include "util.h"
#ifndef FEAT_ASYNCDNS
@@ -50,10 +52,11 @@ struct DNS_Async_Instance {
const char *name;
DNS_NameResolveHandler handler;
void *arg;
int pipe[2];
};
static void
resolve_name(void *anything)
resolve_name(int fd, int event, void *anything)
{
struct DNS_Async_Instance *inst;
IPAddr addrs[DNS_MAX_ADDRESSES];
@@ -61,6 +64,11 @@ resolve_name(void *anything)
int i;
inst = (struct DNS_Async_Instance *)anything;
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES);
for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES &&
@@ -82,7 +90,16 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->handler = handler;
inst->arg = anything;
SCH_AddTimeoutByDelay(0.0, resolve_name, inst);
if (pipe(inst->pipe))
LOG_FATAL("pipe() failed");
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, resolve_name, inst);
if (write(inst->pipe[1], "", 1) < 0)
;
}
#endif /* !FEAT_ASYNCDNS */
@@ -291,11 +308,17 @@ NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
}
void
NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
memset(report, 0, sizeof (*report));
}
int
NSR_GetNTPReport(RPT_NTPReport *report)
{
return 0;
}
void
NSR_GetActivityReport(RPT_ActivityReport *report)
{
@@ -361,9 +384,35 @@ RCL_StartRefclocks(void)
}
void
RCL_ReportSource(RPT_SourceReport *report, struct timeval *now)
RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
{
memset(report, 0, sizeof (*report));
}
#endif /* !FEAT_REFCLOCK */
#ifndef FEAT_SIGND
void
NSD_Initialise(void)
{
}
void
NSD_Finalise(void)
{
}
int
NSD_GetAuthDelay(uint32_t key_id)
{
return 0;
}
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
return 0;
}
#endif /* !FEAT_SIGND */

24
sys.c
View File

@@ -30,6 +30,7 @@
#include "sysincl.h"
#include "sys.h"
#include "sys_null.h"
#include "logging.h"
#if defined(LINUX)
@@ -44,9 +45,18 @@
/* ================================================== */
static int null_driver;
/* ================================================== */
void
SYS_Initialise(void)
SYS_Initialise(int clock_control)
{
null_driver = !clock_control;
if (null_driver) {
SYS_Null_Initialise();
return;
}
#if defined(LINUX)
SYS_Linux_Initialise();
#elif defined(SOLARIS)
@@ -65,6 +75,10 @@ SYS_Initialise(void)
void
SYS_Finalise(void)
{
if (null_driver) {
SYS_Null_Finalise();
return;
}
#if defined(LINUX)
SYS_Linux_Finalise();
#elif defined(SOLARIS)
@@ -91,7 +105,7 @@ void SYS_DropRoot(uid_t uid, gid_t gid)
#elif defined(MACOSX) && defined(FEAT_PRIVDROP)
SYS_MacOSX_DropRoot(uid, gid);
#else
LOG_FATAL(LOGF_Sys, "dropping root privileges not supported");
LOG_FATAL("dropping root privileges not supported");
#endif
}
@@ -102,7 +116,7 @@ void SYS_EnableSystemCallFilter(int level)
#if defined(LINUX) && defined(FEAT_SCFILTER)
SYS_Linux_EnableSystemCallFilter(level);
#else
LOG_FATAL(LOGF_Sys, "system call filter not supported");
LOG_FATAL("system call filter not supported");
#endif
}
@@ -115,7 +129,7 @@ void SYS_SetScheduler(int SchedPriority)
#elif defined(MACOSX)
SYS_MacOSX_SetScheduler(SchedPriority);
#else
LOG_FATAL(LOGF_Sys, "scheduler priority setting not supported");
LOG_FATAL("scheduler priority setting not supported");
#endif
}
@@ -126,7 +140,7 @@ void SYS_LockMemory(void)
#if defined(LINUX) && defined(HAVE_MLOCKALL)
SYS_Linux_MemLockAll(1);
#else
LOG_FATAL(LOGF_Sys, "memory locking not supported");
LOG_FATAL("memory locking not supported");
#endif
}

2
sys.h
View File

@@ -30,7 +30,7 @@
#define GOT_SYS_H
/* Called at the start of the run to do initialisation */
extern void SYS_Initialise(void);
extern void SYS_Initialise(int clock_control);
/* Called at the end of the run to do final clean-up */
extern void SYS_Finalise(void);

View File

@@ -71,7 +71,7 @@ static double offset_register;
static double slew_freq;
/* Time (raw) of last update of slewing frequency and offset */
static struct timeval slew_start;
static struct timespec slew_start;
/* Limits for the slew timeout */
#define MIN_SLEW_TIMEOUT 1.0
@@ -106,7 +106,7 @@ static void update_slew(void);
/* Adjust slew_start on clock step */
static void
handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type == LCL_ChangeUnknownStep) {
@@ -115,7 +115,7 @@ handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
offset_register = 0.0;
update_slew();
} else if (change_type == LCL_ChangeStep) {
UTI_AddDoubleToTimeval(&slew_start, -doffset, &slew_start);
UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
}
}
@@ -129,7 +129,7 @@ start_fastslew(void)
drv_accrue_offset(offset_register, 0.0);
DEBUG_LOG(LOGF_SysGeneric, "fastslew offset=%e", offset_register);
DEBUG_LOG("fastslew offset=%e", offset_register);
offset_register = 0.0;
fastslew_active = 1;
@@ -138,7 +138,7 @@ start_fastslew(void)
/* ================================================== */
static void
stop_fastslew(struct timeval *now)
stop_fastslew(struct timespec *now)
{
double corr;
@@ -169,7 +169,7 @@ clamp_freq(double freq)
static void
update_slew(void)
{
struct timeval now, end_of_slew;
struct timespec now, end_of_slew;
double old_slew_freq, total_freq, corr_freq, duration;
/* Remove currently running timeout */
@@ -178,7 +178,7 @@ update_slew(void)
LCL_ReadRawTime(&now);
/* Adjust the offset register by achieved slew */
UTI_DiffTimevalsToDouble(&duration, &now, &slew_start);
duration = UTI_DiffTimespecsToDouble(&now, &slew_start);
offset_register -= slew_freq * duration;
stop_fastslew(&now);
@@ -242,11 +242,11 @@ update_slew(void)
}
/* Restart timer for the next update */
UTI_AddDoubleToTimeval(&now, duration, &end_of_slew);
UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
slew_start = now;
DEBUG_LOG(LOGF_SysGeneric, "slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
offset_register, correction_rate, base_freq, total_freq, slew_freq,
duration, slew_error);
}
@@ -294,12 +294,12 @@ accrue_offset(double offset, double corr_rate)
/* Determine the correction to generate the cooked time for given raw time */
static void
offset_convert(struct timeval *raw,
offset_convert(struct timespec *raw,
double *corr, double *err)
{
double duration, fastslew_corr, fastslew_err;
UTI_DiffTimevalsToDouble(&duration, raw, &slew_start);
duration = UTI_DiffTimespecsToDouble(raw, &slew_start);
if (drv_get_offset_correction && fastslew_active) {
drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
@@ -324,19 +324,21 @@ offset_convert(struct timeval *raw,
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time;
struct timespec old_time, new_time;
struct timeval new_time_tv;
double err;
LCL_ReadRawTime(&old_time);
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed");
if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG("settimeofday() failed");
return 0;
}
LCL_ReadRawTime(&old_time);
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
err = UTI_DiffTimespecsToDouble(&old_time, &new_time);
lcl_InvokeDispersionNotifyHandlers(fabs(err));
@@ -403,7 +405,7 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
void
SYS_Generic_Finalise(void)
{
struct timeval now;
struct timespec now;
/* Must *NOT* leave a slew running - clock could drift way off
if the daemon is not restarted */

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -47,24 +47,29 @@
#include <sys/capability.h>
#endif
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_SCFILTER
#include <sys/prctl.h>
#include <seccomp.h>
#include <termios.h>
#ifdef FEAT_PHC
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_PPS
#include <linux/pps.h>
#endif
#ifdef FEAT_RTC
#include <linux/rtc.h>
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
#include <linux/sockios.h>
#endif
#endif
#include "sys_linux.h"
#include "sys_timex.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "privops.h"
#include "util.h"
@@ -235,7 +240,7 @@ guess_hz(void)
}
/* oh dear. doomed. */
LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %d", tick);
LOG_FATAL("Can't determine hz from tick %d", tick);
return 0;
}
@@ -271,6 +276,22 @@ kernelvercmp(int major1, int minor1, int patch1,
}
/* ================================================== */
static void
get_kernel_version(int *major, int *minor, int *patch)
{
struct utsname uts;
if (uname(&uts) < 0)
LOG_FATAL("uname() failed");
*patch = 0;
if (sscanf(uts.release, "%d.%d.%d", major, minor, patch) < 2)
LOG_FATAL("Could not parse kernel version");
}
/* ================================================== */
/* Compute the scaling to use on any frequency we set, according to
the vintage of the Linux kernel being used. */
@@ -278,7 +299,6 @@ static void
get_version_specific_details(void)
{
int major, minor, patch;
struct utsname uts;
hz = get_hz();
@@ -293,19 +313,11 @@ get_version_specific_details(void)
(CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
tick_update_hz = 100;
if (uname(&uts) < 0) {
LOG_FATAL(LOGF_SysLinux, "Cannot uname(2) to get kernel version, sorry.");
}
patch = 0;
if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) < 2) {
LOG_FATAL(LOGF_SysLinux, "Cannot read information from uname, sorry");
}
DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
get_kernel_version(&major, &minor, &patch);
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
LOG_FATAL(LOGF_SysLinux, "Kernel version not supported, sorry.");
LOG_FATAL("Kernel version not supported, sorry.");
}
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
@@ -322,7 +334,7 @@ get_version_specific_details(void)
have_setoffset = 1;
}
DEBUG_LOG(LOGF_SysLinux, "hz=%d nominal_tick=%d max_tick_bias=%d",
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d",
hz, nominal_tick, max_tick_bias);
}
@@ -379,7 +391,7 @@ SYS_Linux_Initialise(void)
reset_adjtime_offset();
if (have_setoffset && !test_step_offset()) {
LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET");
LOG(LOGS_INFO, "adjtimex() doesn't support ADJ_SETOFFSET");
have_setoffset = 0;
}
@@ -409,7 +421,7 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
cap_t cap;
if (prctl(PR_SET_KEEPCAPS, 1)) {
LOG_FATAL(LOGF_SysLinux, "prctl() failed");
LOG_FATAL("prctl() failed");
}
UTI_DropRoot(uid, gid);
@@ -419,11 +431,11 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
"cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep";
if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL(LOGF_SysLinux, "cap_from_text() failed");
LOG_FATAL("cap_from_text() failed");
}
if (cap_set_proc(cap)) {
LOG_FATAL(LOGF_SysLinux, "cap_set_proc() failed");
LOG_FATAL("cap_set_proc() failed");
}
cap_free(cap);
@@ -442,7 +454,7 @@ void check_seccomp_applicability(void)
CNF_GetMailOnChange(&mail_enabled, &mail_threshold, &mail_user);
if (mail_enabled)
LOG_FATAL(LOGF_SysLinux, "mailonchange directive cannot be used with -F enabled");
LOG_FATAL("mailonchange directive cannot be used with -F enabled");
}
/* ================================================== */
@@ -452,32 +464,33 @@ SYS_Linux_EnableSystemCallFilter(int level)
{
const int syscalls[] = {
/* Clock */
SCMP_SYS(adjtimex), SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday),
SCMP_SYS(time),
SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday),
SCMP_SYS(settimeofday), SCMP_SYS(time),
/* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getrlimit),
SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigprocmask),
SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), SCMP_SYS(wait4),
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid),
SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn),
SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn),
SCMP_SYS(wait4),
/* Memory */
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2),
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
/* Filesystem */
SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(lseek), SCMP_SYS(rename),
SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64),
SCMP_SYS(unlink),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64),
SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
/* Socket */
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
SCMP_SYS(recvfrom), SCMP_SYS(recvmsg), SCMP_SYS(sendmmsg),
SCMP_SYS(sendmsg), SCMP_SYS(sendto),
SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
/* TODO: check socketcall arguments */
SCMP_SYS(socketcall),
/* General I/O */
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(pipe),
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe),
SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
SCMP_SYS(set_robust_list), SCMP_SYS(write),
/* Miscellaneous */
SCMP_SYS(uname),
SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname),
};
const int socket_domains[] = {
@@ -493,21 +506,30 @@ SYS_Linux_EnableSystemCallFilter(int level)
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
{ SOL_SOCKET, SO_TIMESTAMP },
{ SOL_SOCKET, SO_TIMESTAMP }, { SOL_SOCKET, SO_TIMESTAMPNS },
#ifdef HAVE_LINUX_TIMESTAMPING
{ SOL_SOCKET, SO_SELECT_ERR_QUEUE }, { SOL_SOCKET, SO_TIMESTAMPING },
#endif
};
const static int fcntls[] = { F_GETFD, F_SETFD };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,
#ifdef FEAT_PPS
PTP_SYS_OFFSET,
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_EXTTS_REQUEST, PTP_PIN_SETFUNC, PTP_SYS_OFFSET,
#ifdef PTP_SYS_OFFSET_PRECISE
PTP_SYS_OFFSET_PRECISE,
#endif
#endif
#ifdef FEAT_PPS
PPS_FETCH,
#endif
#ifdef FEAT_RTC
RTC_RD_TIME, RTC_SET_TIME, RTC_UIE_ON, RTC_UIE_OFF,
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
SIOCETHTOOL,
#endif
};
@@ -525,7 +547,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
if (ctx == NULL)
LOG_FATAL(LOGF_SysLinux, "Failed to initialize seccomp");
LOG_FATAL("Failed to initialize seccomp");
/* Add system calls that are always allowed */
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
@@ -566,14 +588,14 @@ SYS_Linux_EnableSystemCallFilter(int level)
}
if (seccomp_load(ctx) < 0)
LOG_FATAL(LOGF_SysLinux, "Failed to load seccomp rules");
LOG_FATAL("Failed to load seccomp rules");
LOG(LOGS_INFO, LOGF_SysLinux, "Loaded seccomp filter");
LOG(LOGS_INFO, "Loaded seccomp filter");
seccomp_release(ctx);
return;
add_failed:
LOG_FATAL(LOGF_SysLinux, "Failed to add seccomp rules");
LOG_FATAL("Failed to add seccomp rules");
}
#endif
@@ -587,7 +609,7 @@ void SYS_Linux_SetScheduler(int SchedPriority)
struct sched_param sched;
if (SchedPriority < 1 || SchedPriority > 99) {
LOG_FATAL(LOGF_SysLinux, "Bad scheduler priority: %d", SchedPriority);
LOG_FATAL("Bad scheduler priority: %d", SchedPriority);
} else {
sched.sched_priority = SchedPriority;
pmax = sched_get_priority_max(SCHED_FIFO);
@@ -599,10 +621,10 @@ void SYS_Linux_SetScheduler(int SchedPriority)
sched.sched_priority = pmin;
}
if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) {
LOG(LOGS_ERR, LOGF_SysLinux, "sched_setscheduler() failed");
LOG(LOGS_ERR, "sched_setscheduler() failed");
}
else {
DEBUG_LOG(LOGF_SysLinux, "Enabled SCHED_FIFO with priority %d",
DEBUG_LOG("Enabled SCHED_FIFO with priority %d",
sched.sched_priority);
}
}
@@ -620,16 +642,230 @@ void SYS_Linux_MemLockAll(int LockAll)
rlim.rlim_max = RLIM_INFINITY;
rlim.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
LOG(LOGS_ERR, LOGF_SysLinux, "setrlimit() failed: not locking into RAM");
LOG(LOGS_ERR, "setrlimit() failed: not locking into RAM");
}
else {
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
LOG(LOGS_ERR, LOGF_SysLinux, "mlockall() failed");
LOG(LOGS_ERR, "mlockall() failed");
}
else {
DEBUG_LOG(LOGF_SysLinux, "Successfully locked into RAM");
DEBUG_LOG("Successfully locked into RAM");
}
}
}
}
#endif /* HAVE_MLOCKALL */
/* ================================================== */
int
SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
{
int major, minor, patch;
get_kernel_version(&major, &minor, &patch);
return kernelvercmp(req_major, req_minor, 0, major, minor, patch) <= 0;
}
/* ================================================== */
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#define PHC_READINGS 10
static int
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
struct ptp_sys_offset sys_off;
struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
double min_delay = 0.0, delays[PHC_READINGS], phc_sum, sys_sum, sys_prec;
int i, n;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
return 0;
}
for (i = 0; i < PHC_READINGS; i++) {
ts1.tv_sec = sys_off.ts[i * 2].sec;
ts1.tv_nsec = sys_off.ts[i * 2].nsec;
ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
sys_tss[i] = ts1;
phc_tss[i] = ts2;
delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
if (delays[i] < 0.0) {
/* Step in the middle of a PHC reading? */
DEBUG_LOG("Bad PTP_SYS_OFFSET sample delay=%e", delays[i]);
return 0;
}
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
sys_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = n = 0, phc_sum = sys_sum = 0.0; i < PHC_READINGS; i++) {
if (delays[i] > min_delay + MAX(sys_prec, precision))
continue;
phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
sys_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
n++;
}
assert(n);
UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
UTI_AddDoubleToTimespec(&sys_tss[0], sys_sum / n, sys_ts);
*err = MAX(min_delay / 2.0, precision);
return 1;
}
/* ================================================== */
static int
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
#ifdef PTP_SYS_OFFSET_PRECISE
struct ptp_sys_offset_precise sys_off;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
if (ioctl(phc_fd, PTP_SYS_OFFSET_PRECISE, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_PRECISE",
strerror(errno));
return 0;
}
phc_ts->tv_sec = sys_off.device.sec;
phc_ts->tv_nsec = sys_off.device.nsec;
sys_ts->tv_sec = sys_off.sys_realtime.sec;
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
return 1;
#else
return 0;
#endif
}
/* ================================================== */
int
SYS_Linux_OpenPHC(const char *path, int phc_index)
{
struct ptp_clock_caps caps;
char phc_path[64];
int phc_fd;
if (!path) {
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
return -1;
path = phc_path;
}
phc_fd = open(path, O_RDONLY);
if (phc_fd < 0) {
LOG(LOGS_ERR, "Could not open %s : %s", path, strerror(errno));
return -1;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG(LOGS_ERR, "ioctl(%s) failed : %s", "PTP_CLOCK_GETCAPS", strerror(errno));
close(phc_fd);
return -1;
}
UTI_FdSetCloexec(phc_fd);
return phc_fd;
}
/* ================================================== */
int
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
{
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 2;
return 1;
} else if ((*reading_mode == 1 || !*reading_mode) &&
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 1;
return 1;
}
return 0;
}
/* ================================================== */
int
SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
int rising, int falling, int enable)
{
struct ptp_pin_desc pin_desc;
struct ptp_extts_request extts_req;
memset(&pin_desc, 0, sizeof (pin_desc));
pin_desc.index = pin;
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
pin_desc.chan = channel;
if (ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
return 0;
}
memset(&extts_req, 0, sizeof (extts_req));
extts_req.index = channel;
extts_req.flags = (enable ? PTP_ENABLE_FEATURE : 0) |
(rising ? PTP_RISING_EDGE : 0) |
(falling ? PTP_FALLING_EDGE : 0);
if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_EXTTS_REQUEST", strerror(errno));
return 0;
}
return 1;
}
/* ================================================== */
int
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
{
struct ptp_extts_event extts_event;
if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
DEBUG_LOG("Could not read PHC extts event");
return 0;
}
phc_ts->tv_sec = extts_event.t.sec;
phc_ts->tv_nsec = extts_event.t.nsec;
*channel = extts_event.index;
return 1;
}
#endif

View File

@@ -39,4 +39,16 @@ extern void SYS_Linux_MemLockAll(int LockAll);
extern void SYS_Linux_SetScheduler(int SchedPriority);
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
extern int SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err);
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
int rising, int falling, int enable);
extern int SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel);
#endif /* GOT_SYS_LINUX_H */

View File

@@ -23,7 +23,7 @@
=======================================================================
Driver file for the MacOS X operating system.
Driver file for the macOS operating system.
*/
@@ -39,23 +39,33 @@
#include "sys_macosx.h"
#include "conf.h"
#include "local.h"
#include "localp.h"
#include "logging.h"
#include "sched.h"
#include "privops.h"
#include "util.h"
#ifdef HAVE_MACOS_SYS_TIMEX
#include <dlfcn.h>
#include "sys_netbsd.h"
#include "sys_timex.h"
static int have_ntp_adjtime = 0;
static int have_bad_adjtime = 0;
#endif
/* ================================================== */
/* This register contains the number of seconds by which the local
clock was estimated to be fast of reference time at the epoch when
gettimeofday() returned T0 */
LCL_ReadRawTime() returned T0 */
static double offset_register;
/* This register contains the epoch to which the offset is referenced */
static struct timeval T0;
static struct timespec T0;
/* This register contains the current estimate of the system
frequency, in absolute (NOT ppm) */
@@ -79,7 +89,7 @@ static double adjustment_requested;
static double drift_removal_interval;
static double current_drift_removal_interval;
static struct timeval Tdrift;
static struct timespec Tdrift;
/* weighting applied to error in calculating drift_removal_interval */
#define ERROR_WEIGHT (0.5)
@@ -91,7 +101,7 @@ static struct timeval Tdrift;
/* RTC synchronisation - once an hour */
static struct timeval last_rtc_sync;
static struct timespec last_rtc_sync;
#define RTC_SYNC_INTERVAL (60 * 60.0)
/* ================================================== */
@@ -107,9 +117,7 @@ clock_initialise(void)
drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
current_drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
if (gettimeofday(&T0, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
LCL_ReadRawTime(&T0);
Tdrift = T0;
last_rtc_sync = T0;
@@ -117,7 +125,7 @@ clock_initialise(void)
newadj.tv_usec = 0;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
}
@@ -135,21 +143,19 @@ static void
start_adjust(void)
{
struct timeval newadj, oldadj;
struct timeval T1;
struct timespec T1;
double elapsed, accrued_error, predicted_error, drift_removal_elapsed;
double adjust_required;
double rounding_error;
double old_adjust_remaining;
/* Determine the amount of error built up since the last adjustment */
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
LCL_ReadRawTime(&T1);
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
elapsed = UTI_DiffTimespecsToDouble(&T1, &T0);
accrued_error = elapsed * current_freq;
UTI_DiffTimevalsToDouble(&drift_removal_elapsed, &T1, &Tdrift);
drift_removal_elapsed = UTI_DiffTimespecsToDouble(&T1, &Tdrift);
/* To allow for the clock being stepped either forward or backwards, clamp
the elapsed time to bounds [ 0.0, current_drift_removal_interval ] */
@@ -157,22 +163,21 @@ start_adjust(void)
predicted_error = (current_drift_removal_interval - drift_removal_elapsed) / 2.0 * current_freq;
DEBUG_LOG(LOGF_SysMacOSX, "drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
1.0e6 * drift_removal_elapsed,
1.0e6 * current_drift_removal_interval,
1.0e6 * predicted_error);
DEBUG_LOG("drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
1.0e6 * drift_removal_elapsed, 1.0e6 * current_drift_removal_interval,
1.0e6 * predicted_error);
adjust_required = - (accrued_error + offset_register + predicted_error);
UTI_DoubleToTimeval(adjust_required, &newadj);
UTI_TimevalToDouble(&newadj, &adjustment_requested);
adjustment_requested = UTI_TimevalToDouble(&newadj);
rounding_error = adjust_required - adjustment_requested;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
old_adjust_remaining = UTI_TimevalToDouble(&oldadj);
offset_register = rounding_error - old_adjust_remaining - predicted_error;
@@ -184,7 +189,7 @@ start_adjust(void)
static void
stop_adjust(void)
{
struct timeval T1;
struct timespec T1;
struct timeval zeroadj, remadj;
double adjustment_remaining, adjustment_achieved;
double elapsed, elapsed_plus_adjust;
@@ -193,15 +198,13 @@ stop_adjust(void)
zeroadj.tv_usec = 0;
if (PRV_AdjustTime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
LCL_ReadRawTime(&T1);
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
elapsed = UTI_DiffTimespecsToDouble(&T1, &T0);
adjustment_remaining = UTI_TimevalToDouble(&remadj);
adjustment_achieved = adjustment_requested - adjustment_remaining;
elapsed_plus_adjust = elapsed - adjustment_achieved;
@@ -233,22 +236,22 @@ accrue_offset(double offset, double corr_rate)
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, T1;
struct timespec old_time, new_time, T1;
struct timeval new_time_tv;
stop_adjust();
if (gettimeofday(&old_time, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
LCL_ReadRawTime(&old_time);
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG("settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, -offset, &T1);
UTI_AddDoubleToTimespec(&T0, -offset, &T1);
T0 = T1;
start_adjust();
@@ -279,7 +282,7 @@ read_frequency(void)
/* ================================================== */
static void
get_offset_correction(struct timeval *raw,
get_offset_correction(struct timespec *raw,
double *corr, double *err)
{
stop_adjust();
@@ -311,9 +314,7 @@ drift_removal_timeout(SCH_ArbitraryArgument not_used)
stop_adjust();
if (gettimeofday(&Tdrift, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
LCL_ReadRawTime(&Tdrift);
current_drift_removal_interval = drift_removal_interval;
@@ -336,23 +337,23 @@ set_sync_status(int synchronised, double est_error, double max_error)
drift_removal_interval = MAX(drift_removal_interval, DRIFT_REMOVAL_INTERVAL);
} else {
if (CNF_GetRtcSync()) {
struct timeval now;
struct timespec now;
double rtc_sync_elapsed;
SCH_GetLastEventTime(NULL, NULL, &now);
UTI_DiffTimevalsToDouble(&rtc_sync_elapsed, &now, &last_rtc_sync);
rtc_sync_elapsed = UTI_DiffTimespecsToDouble(&now, &last_rtc_sync);
if (fabs(rtc_sync_elapsed) >= RTC_SYNC_INTERVAL) {
/* update the RTC by applying a step of 0.0 secs */
apply_step_offset(0.0);
last_rtc_sync = now;
DEBUG_LOG(LOGF_SysMacOSX, "rtc synchronised");
DEBUG_LOG("rtc synchronised");
}
}
interval = ERROR_WEIGHT * est_error / (fabs(current_freq) + FREQUENCY_RES);
drift_removal_interval = MAX(interval, DRIFT_REMOVAL_INTERVAL_MIN);
DEBUG_LOG(LOGF_SysMacOSX, "est_error: %.3f current_freq: %.3f est drift_removal_interval: %.3f act drift_removal_interval: %.3f",
DEBUG_LOG("est_error: %.3f current_freq: %.3f est drift_removal_interval: %.3f act drift_removal_interval: %.3f",
est_error * 1.0e6, current_freq * 1.0e6, interval, drift_removal_interval);
}
@@ -396,7 +397,7 @@ set_realtime(void)
THREAD_TIME_CONSTRAINT_POLICY_COUNT);
if (kr != KERN_SUCCESS) {
LOG(LOGS_WARN, LOGF_SysMacOSX, "Cannot set real-time priority: %d", kr);
LOG(LOGS_WARN, "Cannot set real-time priority: %d", kr);
return -1;
}
return 0;
@@ -425,8 +426,8 @@ void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid)
/* ================================================== */
void
SYS_MacOSX_Initialise(void)
static void
legacy_MacOSX_Initialise(void)
{
clock_initialise();
@@ -442,8 +443,8 @@ SYS_MacOSX_Initialise(void)
/* ================================================== */
void
SYS_MacOSX_Finalise(void)
static void
legacy_MacOSX_Finalise(void)
{
SCH_RemoveTimeout(drift_removal_id);
@@ -452,4 +453,67 @@ SYS_MacOSX_Finalise(void)
/* ================================================== */
#ifdef HAVE_MACOS_SYS_TIMEX
/*
Test adjtime() to see if Apple have fixed the signed/unsigned bug
*/
static int
test_adjtime()
{
struct timeval tv1 = {-1, 0};
struct timeval tv2 = {0, 0};
struct timeval tv;
if (PRV_AdjustTime(&tv1, &tv) != 0) {
return 0;
}
if (PRV_AdjustTime(&tv2, &tv) != 0) {
return 0;
}
if (tv.tv_sec < -1 || tv.tv_sec > 1) {
return 0;
}
return 1;
}
#endif
/* ================================================== */
void
SYS_MacOSX_Initialise(void)
{
#ifdef HAVE_MACOS_SYS_TIMEX
have_ntp_adjtime = (dlsym(RTLD_NEXT, "ntp_adjtime") != NULL);
if (have_ntp_adjtime) {
have_bad_adjtime = !test_adjtime();
if (have_bad_adjtime) {
LOG(LOGS_WARN, "adjtime() is buggy - using timex driver");
SYS_Timex_Initialise();
} else {
SYS_NetBSD_Initialise();
}
return;
}
#endif
legacy_MacOSX_Initialise();
}
/* ================================================== */
void
SYS_MacOSX_Finalise(void)
{
#ifdef HAVE_MACOS_SYS_TIMEX
if (have_ntp_adjtime) {
if (have_bad_adjtime) {
SYS_Timex_Finalise();
} else {
SYS_NetBSD_Finalise();
}
return;
}
#endif
legacy_MacOSX_Finalise();
}
#endif

View File

@@ -23,7 +23,7 @@
=======================================================================
Header file for MacOS X driver
Header file for macOS driver
*/

View File

@@ -60,34 +60,35 @@ static void
accrue_offset(double offset, double corr_rate)
{
struct timeval newadj, oldadj;
double doldadj;
UTI_DoubleToTimeval(-offset, &newadj);
if (PRV_AdjustTime(&newadj, &oldadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
LOG_FATAL("adjtime() failed");
/* Add the old remaining adjustment if not zero */
UTI_TimevalToDouble(&oldadj, &offset);
if (offset != 0.0) {
UTI_AddDoubleToTimeval(&newadj, offset, &newadj);
doldadj = UTI_TimevalToDouble(&oldadj);
if (doldadj != 0.0) {
UTI_DoubleToTimeval(-offset + doldadj, &newadj);
if (PRV_AdjustTime(&newadj, NULL) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
LOG_FATAL("adjtime() failed");
}
}
/* ================================================== */
static void
get_offset_correction(struct timeval *raw,
get_offset_correction(struct timespec *raw,
double *corr, double *err)
{
struct timeval remadj;
double adjustment_remaining;
if (PRV_AdjustTime(NULL, &remadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
LOG_FATAL("adjtime() failed");
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
adjustment_remaining = UTI_TimevalToDouble(&remadj);
*corr = adjustment_remaining;
if (err) {
@@ -137,7 +138,7 @@ SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
/* Check if we have write access to /dev/clockctl */
fd = open("/dev/clockctl", O_WRONLY);
if (fd < 0)
LOG_FATAL(LOGF_SysNetBSD, "Can't write to /dev/clockctl");
LOG_FATAL("Can't write to /dev/clockctl");
close(fd);
#endif
}

140
sys_null.c Normal file
View File

@@ -0,0 +1,140 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Null clock driver for operation with no clock control.
*/
#include "config.h"
#include "sysincl.h"
#include "sys_null.h"
#include "local.h"
#include "localp.h"
#include "logging.h"
#include "util.h"
/* Current frequency offset of the system clock (in ppm) */
static double freq;
/* Offset of the system clock at the last update */
static double offset_register;
/* Time of the last update */
static struct timespec last_update;
/* Minimum interval between updates when frequency is constant */
#define MIN_UPDATE_INTERVAL 1000.0
/* ================================================== */
static void
update_offset(void)
{
struct timespec now;
double duration;
LCL_ReadRawTime(&now);
duration = UTI_DiffTimespecsToDouble(&now, &last_update);
offset_register += 1.0e-6 * freq * duration;
last_update = now;
DEBUG_LOG("System clock offset=%e freq=%f", offset_register, freq);
}
/* ================================================== */
static double
read_frequency(void)
{
return freq;
}
/* ================================================== */
static double
set_frequency(double freq_ppm)
{
update_offset();
freq = freq_ppm;
return freq;
}
/* ================================================== */
static void
accrue_offset(double offset, double corr_rate)
{
offset_register += offset;
}
/* ================================================== */
static int
apply_step_offset(double offset)
{
return 0;
}
/* ================================================== */
static void
offset_convert(struct timespec *raw, double *corr, double *err)
{
double duration;
duration = UTI_DiffTimespecsToDouble(raw, &last_update);
if (duration > MIN_UPDATE_INTERVAL) {
update_offset();
duration = 0.0;
}
*corr = -1.0e-6 * freq * duration - offset_register;
if (err)
*err = 0.0;
}
/* ================================================== */
void
SYS_Null_Initialise(void)
{
offset_register = 0.0;
LCL_ReadRawTime(&last_update);
lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset,
apply_step_offset, offset_convert, NULL, NULL);
LOG(LOGS_INFO, "Disabled control of system clock");
}
/* ================================================== */
void
SYS_Null_Finalise(void)
{
}

34
sys_null.h Normal file
View File

@@ -0,0 +1,34 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for null clock driver
*/
#ifndef GOT_SYS_NULL_H
#define GOT_SYS_NULL_H
extern void SYS_Null_Initialise(void);
extern void SYS_Null_Finalise(void);
#endif

View File

@@ -61,7 +61,10 @@
#define MIN_TICK_RATE 100
/* Saved timex status */
static int status;
static int sys_status;
/* Saved TAI-UTC offset */
static int sys_tai_offset;
/* ================================================== */
@@ -95,33 +98,47 @@ set_frequency(double freq_ppm)
/* ================================================== */
static void
set_leap(int leap)
set_leap(int leap, int tai_offset)
{
struct timex txc;
int applied;
int applied, prev_status;
applied = 0;
if (!leap) {
txc.modes = 0;
if (SYS_Timex_Adjust(&txc, 1) == TIME_WAIT)
applied = 1;
}
txc.modes = 0;
applied = SYS_Timex_Adjust(&txc, 0) == TIME_WAIT;
status &= ~(STA_INS | STA_DEL);
prev_status = sys_status;
sys_status &= ~(STA_INS | STA_DEL);
if (leap > 0)
status |= STA_INS;
sys_status |= STA_INS;
else if (leap < 0)
status |= STA_DEL;
sys_status |= STA_DEL;
txc.modes = MOD_STATUS;
txc.status = status;
txc.status = sys_status;
#ifdef MOD_TAI
if (tai_offset) {
txc.modes |= MOD_TAI;
txc.constant = tai_offset;
if (applied && !(sys_status & (STA_INS | STA_DEL)))
sys_tai_offset += prev_status & STA_INS ? 1 : -1;
if (sys_tai_offset != tai_offset) {
sys_tai_offset = tai_offset;
LOG(LOGS_INFO, "System clock TAI offset set to %d seconds", tai_offset);
}
}
#endif
SYS_Timex_Adjust(&txc, 0);
LOG(LOGS_INFO, LOGF_SysTimex, "System clock status %s leap second",
leap ? (leap > 0 ? "set to insert" : "set to delete") :
(applied ? "reset after" : "set to not insert/delete"));
if (prev_status != sys_status) {
LOG(LOGS_INFO, "System clock status %s leap second",
leap ? (leap > 0 ? "set to insert" : "set to delete") :
(applied ? "reset after" : "set to not insert/delete"));
}
}
/* ================================================== */
@@ -149,16 +166,17 @@ set_sync_status(int synchronised, double est_error, double max_error)
#endif
if (synchronised)
status &= ~STA_UNSYNC;
sys_status &= ~STA_UNSYNC;
else
status |= STA_UNSYNC;
sys_status |= STA_UNSYNC;
txc.modes = MOD_STATUS | MOD_ESTERROR | MOD_MAXERROR;
txc.status = status;
txc.status = sys_status;
txc.esterror = est_error * 1.0e6;
txc.maxerror = max_error * 1.0e6;
SYS_Timex_Adjust(&txc, 1);
if (SYS_Timex_Adjust(&txc, 1) < 0)
;
}
/* ================================================== */
@@ -168,17 +186,18 @@ initialise_timex(void)
{
struct timex txc;
status = STA_UNSYNC;
sys_status = STA_UNSYNC;
sys_tai_offset = 0;
/* Reset PLL offset */
txc.modes = MOD_OFFSET | MOD_STATUS;
txc.status = STA_PLL | status;
txc.status = STA_PLL | sys_status;
txc.offset = 0;
SYS_Timex_Adjust(&txc, 0);
/* Turn PLL off */
txc.modes = MOD_STATUS;
txc.status = status;
txc.status = sys_status;
SYS_Timex_Adjust(&txc, 0);
}
@@ -238,11 +257,9 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
if (state < 0) {
if (!ignore_error)
LOG_FATAL(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
txc->modes, strerror(errno));
LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
else
DEBUG_LOG(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
txc->modes, strerror(errno));
DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
}
return state;

View File

@@ -59,7 +59,7 @@
#include <time.h>
#include <unistd.h>
#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS)
#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS) || defined(HAVE_MACOS_SYS_TIMEX)
#include <sys/timex.h>
#endif
@@ -76,4 +76,8 @@
#include <arpa/inet.h>
#endif
#ifdef HAVE_GETRANDOM
#include <sys/random.h>
#endif
#endif /* GOT_SYSINCL_H */

View File

@@ -92,23 +92,21 @@ read_timeout(void *arg)
if (fabs(comp) <= MAX_COMP) {
comp = LCL_SetTempComp(comp);
DEBUG_LOG(LOGF_TempComp, "tempcomp updated to %f for %f", comp, temp);
DEBUG_LOG("tempcomp updated to %f for %f", comp, temp);
if (logfileid != -1) {
struct timeval now;
struct timespec now;
LCL_ReadCookedTime(&now, NULL);
LOG_FileWrite(logfileid, "%s %11.4e %11.4e",
UTI_TimeToLogForm(now.tv_sec), temp, comp);
}
} else {
LOG(LOGS_WARN, LOGF_TempComp,
"Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
LOG(LOGS_WARN, "Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
comp, MAX_COMP);
}
} else {
LOG(LOGS_WARN, LOGF_TempComp, "Could not read temperature from %s",
filename);
LOG(LOGS_WARN, "Could not read temperature from %s", filename);
}
if (f)
@@ -126,7 +124,7 @@ read_points(const char *filename)
f = fopen(filename, "r");
if (!f) {
LOG_FATAL(LOGF_TempComp, "Could not open tempcomp point file %s", filename);
LOG_FATAL("Could not open tempcomp point file %s", filename);
return;
}
@@ -135,7 +133,7 @@ read_points(const char *filename)
while (fgets(line, sizeof (line), f)) {
p = (struct Point *)ARR_GetNewElement(points);
if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) {
LOG_FATAL(LOGF_Configure, "Could not read tempcomp point from %s", filename);
LOG_FATAL("Could not read tempcomp point from %s", filename);
break;
}
}
@@ -143,7 +141,7 @@ read_points(const char *filename)
fclose(f);
if (ARR_GetSize(points) < 2)
LOG_FATAL(LOGF_Configure, "Not enough points in %s", filename);
LOG_FATAL("Not enough points in %s", filename);
}
void

View File

@@ -6,6 +6,7 @@ cd ../..
for opts in \
"--enable-debug" \
"--enable-ntp-signd" \
"--enable-scfilter" \
"--disable-asyncdns" \
"--disable-ipv6" \
@@ -16,6 +17,8 @@ for opts in \
"--disable-cmdmon" \
"--disable-ntp" \
"--disable-refclock" \
"--disable-timestamping" \
"--disable-timestamping --disable-ntp" \
"--disable-cmdmon --disable-ntp" \
"--disable-cmdmon --disable-refclock" \
"--disable-cmdmon --disable-ntp --disable-refclock"

14
test/compilation/002-scanbuild Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
cd ../..
for opts in \
"--host-system=Linux" \
"--host-system=NetBSD" \
"--host-system=FreeBSD" \
"--without-nss" \
"--without-tomcrypt --without-nss"
do
./configure $opts
scan-build make "$@" || exit 1
done

17
test/simulation/010-multrecv Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
. ./test.common
export CLKNETSIM_RECV_MULTIPLY=4
test_start "multiple received packets"
limit=50000
client_server_options="minpoll 6 maxpoll 6"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
test_pass

18
test/simulation/011-asymjitter Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
. ./test.common
test_start "asymmetric jitter"
jitter=5e-4
jitter_asymmetry=0.47
limit=100000
max_sync_time=2000
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
test_pass

View File

@@ -37,13 +37,15 @@ for hash in $hashes; do
echo "$key" >> tmp/client.keys
done
for key in $(seq $keys); do
client_server_options="key $key"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
for version in 3 4; do
for key in $(seq $keys); do
client_server_options="version $version key $key"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
done
server_conf=""

View File

@@ -11,7 +11,7 @@ sourcestats"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^Reference ID : 192\.168\.123\.1 \(192\.168\.123\.1\)
check_chronyc_output "^Reference ID : C0A87B01 \(192\.168\.123\.1\)
Stratum : 2
Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time
@@ -20,8 +20,8 @@ RMS offset : 0\.000...... seconds
Frequency : (99|100)\.... ppm fast
Residual freq : [+-][0-9]\.... ppm
Skew : [0-9]\.... ppm
Root delay : 0\.000... seconds
Root dispersion : 0\.000... seconds
Root delay : 0\.000...... seconds
Root dispersion : 0\.000...... seconds
Update interval : [0-9]+\.. seconds
Leap status : Normal
210 Number of sources = 1

View File

@@ -7,6 +7,7 @@ export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
leap=$[2 * 24 * 3600]
limit=$[4 * 24 * 3600]
client_start=$[2 * 3600]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
refclock_jitter=1e-9
@@ -27,14 +28,25 @@ for leapmode in system step slew; do
check_sync || test_fail
done
client_server_options="trust"
client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_options=""
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
for smoothmode in "" "leaponly"; do
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC
leapsecmode slew
smoothtime 400 0.001 $smoothmode"
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -3,7 +3,7 @@
. ./test.common
test_start "presend option"
min_sync_time=140
min_sync_time=136
max_sync_time=260
client_server_options="presend 6 maxdelay 16"
client_conf="maxdistance 10"

View File

@@ -11,10 +11,11 @@ freq_offset="(* 1e-4 (sine 1000))"
base_delay="(* -1.0 (equal 0.1 (min time 4250) 4250))"
client_server_options="minpoll 4 maxpoll 4"
client_conf="fallbackdrift 6 10"
max_sync_time=4500
time_max_limit=1e0
time_rms_limit=1e0
freq_max_limit=5e-4
freq_rms_limit=5e-4
freq_max_limit=2e-4
freq_rms_limit=1e-4
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -10,7 +10,7 @@ wander=0.0
freq_offset="(sum 1e-10)"
time_rms_limit=2e-4
client_server_options="maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevratio 2.0"
client_server_options="maxpoll 6 maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevratio 2.0"
run_test || test_fail
check_chronyd_exit || test_fail
@@ -18,7 +18,7 @@ check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
for client_server_options in "maxdelay 2e-5" \ "maxdelayratio 1.001"; do
for client_server_options in "maxpoll 6 maxdelay 2e-5" "maxpoll 6 maxdelayratio 1.001"; do
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail

View File

@@ -19,18 +19,27 @@ refclock_offset="(* 10.0 (equal 0.1 (max (sum 1.0) 1000) 1000))"
server_step="(* -10.0 (equal 0.1 (sum 1.0) 1))"
server_strata=1
server_conf="refclock SHM 0 dpoll 4 poll 6
smoothtime 2000 1"
smoothtime 2000 1
maxjitter 10.0"
time_offset=-10
client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100"
min_sync_time=8000
max_sync_time=8500
max_sync_time=9000
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
client_server_options="minpoll 6 maxpoll 6 xleave maxdelay 1e-1"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
client_server_options="minpoll 6 maxpoll 6"
min_sync_time=$default_min_sync_time
max_sync_time=$default_max_sync_time
time_max_limit=11

Some files were not shown because too many files have changed in this diff Show More