Compare commits

..

158 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
90 changed files with 3520 additions and 1665 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 \ 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 \ 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@ EXTRA_OBJS=@EXTRA_OBJECTS@
@@ -64,10 +64,10 @@ chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS) $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
distclean : clean distclean : clean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
$(MAKE) -C doc distclean $(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean $(MAKE) -C test/unit distclean
-rm -f .DS_Store
-rm -f Makefile config.h config.log
clean : clean :
-rm -f *.o *.s chronyc chronyd core *~ -rm -f *.o *.s chronyc chronyd core *~
@@ -109,10 +109,14 @@ install-docs :
%.s : %.c %.s : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -S $< $(CC) $(CFLAGS) $(CPPFLAGS) -S $<
check : chronyd chronyc quickcheck : chronyd chronyc
$(MAKE) -C test/unit check $(MAKE) -C test/unit check
cd test/simulation && ./run cd test/simulation && ./run
check : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run -i 20 -m 2
Makefile : Makefile.in configure Makefile : Makefile.in configure
@echo @echo
@echo Makefile needs to be regenerated, run ./configure @echo Makefile needs to be regenerated, run ./configure

49
NEWS
View File

@@ -1,3 +1,49 @@
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 New in version 3.0
================== ==================
@@ -16,8 +62,7 @@ Enhancements
* Add -t option to chronyd to exit after specified time * Add -t option to chronyd to exit after specified time
* Add partial protection against replay attacks on symmetric mode * Add partial protection against replay attacks on symmetric mode
* Don't reset polling interval when switching sources to online state * Don't reset polling interval when switching sources to online state
* Enable NTP response rate limiting by default * Allow rate limiting with very short intervals
(1024 packets per second per IP address and 25% leak)
* Improve maximum server throughput on Linux and NetBSD * Improve maximum server throughput on Linux and NetBSD
* Remove dump files after start * Remove dump files after start
* Add tab-completion to chronyc with libedit/readline * Add tab-completion to chronyc with libedit/readline

5
README
View File

@@ -16,7 +16,7 @@ and systems that do not run continuosly, or run on a virtual machine.
Typical accuracy between two machines synchronised over the Internet is Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of within a few milliseconds; on a LAN, accuracy is typically in tens of
microseconds. With hardware timestamping or a hardware reference clock microseconds. With hardware timestamping, or a hardware reference clock,
sub-microsecond accuracy may be possible. sub-microsecond accuracy may be possible.
Two programs are included in chrony, chronyd is a daemon that can be Two programs are included in chrony, chronyd is a daemon that can be
@@ -203,6 +203,9 @@ Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de> Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values Handling arbitrary HZ values
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr> Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc Patch to add refresh command to chronyc

View File

@@ -50,8 +50,11 @@ typedef struct {
unsigned short port; unsigned short port;
} NTP_Remote_Address; } NTP_Remote_Address;
#define INVALID_IF_INDEX -1
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
int if_index;
int sock_fd; int sock_fd;
} NTP_Local_Address; } NTP_Local_Address;

10
candm.h
View File

@@ -362,8 +362,9 @@ typedef struct {
domain socket. domain socket.
Version 6 (no authentication) : changed format of client accesses by index Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types), new fields and flags in NTP source request (using new request/reply types) and manual timestamp, new fields and flags
and report, new commands: ntpdata, refresh, serverstats in NTP source request and report, new commands: ntpdata, refresh,
serverstats
*/ */
#define PROTO_VERSION_NUMBER 6 #define PROTO_VERSION_NUMBER 6
@@ -461,7 +462,8 @@ typedef struct {
#define RPY_SERVER_STATS 14 #define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15 #define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define RPY_NTP_DATA 16 #define RPY_NTP_DATA 16
#define N_REPLY_TYPES 17 #define RPY_MANUAL_TIMESTAMP2 17
#define N_REPLY_TYPES 18
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -569,7 +571,7 @@ typedef struct {
} RPY_Rtc; } RPY_Rtc;
typedef struct { typedef struct {
uint32_t centiseconds; Float offset;
Float dfreq_ppm; Float dfreq_ppm;
Float new_afreq_ppm; Float new_afreq_ppm;
int32_t EOR; int32_t EOR;

247
client.c
View File

@@ -3,6 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016
* Copyright (C) Miroslav Lichvar 2009-2016 * Copyright (C) Miroslav Lichvar 2009-2016
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -80,8 +81,7 @@ int log_debug_enabled = 0;
void LOG_Message(LOG_Severity severity, void LOG_Message(LOG_Severity severity,
#if DEBUG > 0 #if DEBUG > 0
LOG_Facility facility, int line_number, int line_number, const char *filename, const char *function_name,
const char *filename, const char *function_name,
#endif #endif
const char *format, ...) const char *format, ...)
{ {
@@ -167,18 +167,18 @@ get_sockaddrs(const char *hostnames, int port)
addr = (union sockaddr_all *)ARR_GetNewElement(addrs); addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >= if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >=
sizeof (addr->un.sun_path)) sizeof (addr->un.sun_path))
LOG_FATAL(LOGF_Client, "Unix socket path too long"); LOG_FATAL("Unix socket path too long");
addr->un.sun_family = AF_UNIX; addr->un.sun_family = AF_UNIX;
} else { } else {
if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) { if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
DEBUG_LOG(LOGF_Client, "Could not get IP address for %s", hostname); DEBUG_LOG("Could not get IP address for %s", hostname);
break; continue;
} }
for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) { for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
addr = (union sockaddr_all *)ARR_GetNewElement(addrs); addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr); UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr);
DEBUG_LOG(LOGF_Client, "Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i])); DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
} }
} }
} }
@@ -215,7 +215,7 @@ prepare_socket(union sockaddr_all *addr)
sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
if (sock_fd < 0) { if (sock_fd < 0) {
DEBUG_LOG(LOGF_Client, "Could not create socket : %s", strerror(errno)); DEBUG_LOG("Could not create socket : %s", strerror(errno));
return 0; return 0;
} }
@@ -228,7 +228,7 @@ prepare_socket(union sockaddr_all *addr)
dir = UTI_PathToDir(addr->un.sun_path); dir = UTI_PathToDir(addr->un.sun_path);
if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path), if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path),
"%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path)) "%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path))
LOG_FATAL(LOGF_Client, "Unix socket path too long"); LOG_FATAL("Unix socket path too long");
Free(dir); Free(dir);
sa_un.sun_family = AF_UNIX; sa_un.sun_family = AF_UNIX;
@@ -236,19 +236,19 @@ prepare_socket(union sockaddr_all *addr)
/* Bind the socket to the path */ /* Bind the socket to the path */
if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) { if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) {
DEBUG_LOG(LOGF_Client, "Could not bind socket : %s", strerror(errno)); DEBUG_LOG("Could not bind socket : %s", strerror(errno));
return 0; return 0;
} }
/* Allow server without root privileges to send replies to our socket */ /* Allow server without root privileges to send replies to our socket */
if (chmod(sa_un.sun_path, 0666) < 0) { if (chmod(sa_un.sun_path, 0666) < 0) {
DEBUG_LOG(LOGF_Client, "Could not change socket permissions : %s", strerror(errno)); DEBUG_LOG("Could not change socket permissions : %s", strerror(errno));
return 0; return 0;
} }
} }
if (connect(sock_fd, &addr->sa, addr_len) < 0) { if (connect(sock_fd, &addr->sa, addr_len) < 0) {
DEBUG_LOG(LOGF_Client, "Could not connect socket : %s", strerror(errno)); DEBUG_LOG("Could not connect socket : %s", strerror(errno));
return 0; return 0;
} }
@@ -268,7 +268,7 @@ close_io(void)
/* Remove our Unix domain socket */ /* Remove our Unix domain socket */
if (getsockname(sock_fd, &addr.sa, &addr_len) < 0) if (getsockname(sock_fd, &addr.sa, &addr_len) < 0)
LOG_FATAL(LOGF_Client, "getsockname() failed : %s", strerror(errno)); LOG_FATAL("getsockname() failed : %s", strerror(errno));
if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) && if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) &&
addr.sa.sa_family == AF_UNIX) addr.sa.sa_family == AF_UNIX)
unlink(addr.un.sun_path); unlink(addr.un.sun_path);
@@ -294,8 +294,7 @@ open_io(void)
/* Find an address for which a socket can be opened and connected */ /* Find an address for which a socket can be opened and connected */
for (; address_index < ARR_GetSize(sockaddrs); address_index++) { for (; address_index < ARR_GetSize(sockaddrs); address_index++) {
addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index); addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index);
DEBUG_LOG(LOGF_Client, "Opening connection to %s", DEBUG_LOG("Opening connection to %s", UTI_SockaddrToString(&addr->sa));
UTI_SockaddrToString(&addr->sa));
if (prepare_socket(addr)) if (prepare_socket(addr))
return 1; return 1;
@@ -316,7 +315,7 @@ bits_to_mask(int bits, int family, IPAddr *mask)
mask->family = family; mask->family = family;
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
if (bits < 0) if (bits > 32 || bits < 0)
bits = 32; bits = 32;
if (bits > 0) { if (bits > 0) {
mask->addr.in4 = -1; mask->addr.in4 = -1;
@@ -372,13 +371,13 @@ read_mask_address(char *line, IPAddr *mask, IPAddr *address)
bits_to_mask(-1, address->family, mask); bits_to_mask(-1, address->family, mask);
return 1; return 1;
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname"); LOG(LOGS_ERR, "Could not get address for hostname");
return 0; return 0;
} }
} }
} }
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for mask/address"); LOG(LOGS_ERR, "Invalid syntax for mask/address");
return 0; return 0;
} }
@@ -437,11 +436,11 @@ read_address_integer(char *line, IPAddr *address, int *value)
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (sscanf(line, "%d", value) != 1) { if (sscanf(line, "%d", value) != 1) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address value"); LOG(LOGS_ERR, "Invalid syntax for address value");
ok = 0; ok = 0;
} else { } else {
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname"); LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0; ok = 0;
} else { } else {
ok = 1; ok = 1;
@@ -465,11 +464,11 @@ read_address_double(char *line, IPAddr *address, double *value)
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (sscanf(line, "%lf", value) != 1) { if (sscanf(line, "%lf", value) != 1) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address value"); LOG(LOGS_ERR, "Invalid syntax for address value");
ok = 0; ok = 0;
} else { } else {
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname"); LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0; ok = 0;
} else { } else {
ok = 1; ok = 1;
@@ -702,7 +701,7 @@ process_cmd_burst(CMD_Request *msg, char *line)
CPS_SplitWord(s2); CPS_SplitWord(s2);
if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) { if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for burst command"); LOG(LOGS_ERR, "Invalid syntax for burst command");
return 0; return 0;
} }
@@ -734,7 +733,7 @@ process_cmd_local(CMD_Request *msg, char *line)
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) { } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
on_off = 1; on_off = 1;
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for local command"); LOG(LOGS_ERR, "Invalid syntax for local command");
return 0; return 0;
} }
@@ -763,7 +762,7 @@ process_cmd_manual(CMD_Request *msg, const char *line)
} else if (!strcmp(p, "reset")) { } else if (!strcmp(p, "reset")) {
msg->data.manual.option = htonl(2); msg->data.manual.option = htonl(2);
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for manual command"); LOG(LOGS_ERR, "Invalid syntax for manual command");
return 0; return 0;
} }
msg->command = htons(REQ_MANUAL); msg->command = htons(REQ_MANUAL);
@@ -797,8 +796,8 @@ parse_allow_deny(CMD_Request *msg, char *line)
(n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) { (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) {
/* Try to parse as the name of a machine */ /* Try to parse as the name of a machine */
if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { if (slashpos || DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not read address"); LOG(LOGS_ERR, "Could not read address");
return 0; return 0;
} else { } else {
UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
@@ -851,7 +850,7 @@ parse_allow_deny(CMD_Request *msg, char *line)
if (n == 1) { if (n == 1) {
msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits); msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits);
} else { } else {
LOG(LOGS_WARN, LOGF_Client, "Warning: badly formatted subnet size, using %d", LOG(LOGS_WARN, "Warning: badly formatted subnet size, using %d",
(int)ntohl(msg->data.allow_deny.subnet_bits)); (int)ntohl(msg->data.allow_deny.subnet_bits));
} }
} }
@@ -986,7 +985,7 @@ process_cmd_accheck(CMD_Request *msg, char *line)
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
return 1; return 1;
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Could not read address"); LOG(LOGS_ERR, "Could not read address");
return 0; return 0;
} }
} }
@@ -1002,7 +1001,7 @@ process_cmd_cmdaccheck(CMD_Request *msg, char *line)
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
return 1; return 1;
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Could not read address"); LOG(LOGS_ERR, "Could not read address");
return 0; return 0;
} }
} }
@@ -1072,17 +1071,17 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
status = CPS_ParseNTPSourceAdd(line, &data); status = CPS_ParseNTPSourceAdd(line, &data);
switch (status) { switch (status) {
case 0: case 0:
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for add command"); LOG(LOGS_ERR, "Invalid syntax for add command");
break; break;
default: default:
if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) { if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Invalid host/IP address"); LOG(LOGS_ERR, "Invalid host/IP address");
break; break;
} }
opt_name = NULL; opt_name = NULL;
if (opt_name) { if (opt_name) {
LOG(LOGS_ERR, LOGF_Client, "%s can't be set in chronyc", opt_name); LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name);
break; break;
} }
@@ -1152,11 +1151,11 @@ process_cmd_delete(CMD_Request *msg, char *line)
CPS_SplitWord(line); CPS_SplitWord(line);
if (!*hostname) { if (!*hostname) {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address"); LOG(LOGS_ERR, "Invalid syntax for address");
ok = 0; ok = 0;
} else { } else {
if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) { if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname"); LOG(LOGS_ERR, "Could not get address for hostname");
ok = 0; ok = 0;
} else { } else {
UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr); UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr);
@@ -1241,6 +1240,7 @@ give_help(void)
"Other daemon commands:\0\0" "Other daemon commands:\0\0"
"cyclelogs\0Close and re-open log files\0" "cyclelogs\0Close and re-open log files\0"
"dump\0Dump all measurements to save files\0" "dump\0Dump all measurements to save files\0"
"rekey\0Re-read keys from key file\0"
"\0\0" "\0\0"
"Client commands:\0\0" "Client commands:\0\0"
"dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0" "dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0"
@@ -1367,17 +1367,16 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
memset(((char *)request) + command_length - padding_length, 0, padding_length); memset(((char *)request) + command_length - padding_length, 0, padding_length);
if (sock_fd < 0) { if (sock_fd < 0) {
DEBUG_LOG(LOGF_Client, "No socket to send request"); DEBUG_LOG("No socket to send request");
return 0; return 0;
} }
if (send(sock_fd, (void *)request, command_length, 0) < 0) { if (send(sock_fd, (void *)request, command_length, 0) < 0) {
DEBUG_LOG(LOGF_Client, "Could not send %d bytes : %s", DEBUG_LOG("Could not send %d bytes : %s", command_length, strerror(errno));
command_length, strerror(errno));
return 0; return 0;
} }
DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length); DEBUG_LOG("Sent %d bytes", command_length);
} }
if (gettimeofday(&tv, NULL)) if (gettimeofday(&tv, NULL))
@@ -1392,7 +1391,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) - timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
UTI_DiffTimespecsToDouble(&ts_now, &ts_start); UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
UTI_DoubleToTimeval(timeout, &tv); UTI_DoubleToTimeval(timeout, &tv);
DEBUG_LOG(LOGF_Client, "Timeout %f seconds", timeout); DEBUG_LOG("Timeout %f seconds", timeout);
FD_ZERO(&rdfd); FD_ZERO(&rdfd);
FD_ZERO(&wrfd); FD_ZERO(&wrfd);
@@ -1406,7 +1405,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &tv); select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &tv);
if (select_status < 0) { if (select_status < 0) {
DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno)); DEBUG_LOG("select failed : %s", strerror(errno));
} else if (select_status == 0) { } else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */ /* Timeout must have elapsed, try a resend? */
new_attempt = 1; new_attempt = 1;
@@ -1416,10 +1415,10 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
if (recv_status < 0) { if (recv_status < 0) {
/* If we get connrefused here, it suggests the sendto is /* If we get connrefused here, it suggests the sendto is
going to a dead port */ going to a dead port */
DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno)); DEBUG_LOG("Could not receive : %s", strerror(errno));
new_attempt = 1; new_attempt = 1;
} else { } else {
DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status); DEBUG_LOG("Received %d bytes", recv_status);
read_length = recv_status; read_length = recv_status;
if (read_length >= offsetof(CMD_Reply, data)) { if (read_length >= offsetof(CMD_Reply, data)) {
@@ -1469,7 +1468,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#endif #endif
/* Good packet received, print out results */ /* Good packet received, print out results */
DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d", DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status)); ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
break; break;
} }
@@ -2059,7 +2058,8 @@ process_cmd_sources(char *line)
mode = ntohs(reply.data.source_data.mode); mode = ntohs(reply.data.source_data.mode);
UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr); UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
format_name(name, sizeof (name), 25, mode == RPY_SD_MD_REF, format_name(name, sizeof (name), 25,
mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
ip_addr.addr.in4, &ip_addr); ip_addr.addr.in4, &ip_addr);
switch (mode) { switch (mode) {
@@ -2214,8 +2214,8 @@ process_cmd_tracking(char *line)
"Frequency : %.3F\n" "Frequency : %.3F\n"
"Residual freq : %+.3f ppm\n" "Residual freq : %+.3f ppm\n"
"Skew : %.3f ppm\n" "Skew : %.3f ppm\n"
"Root delay : %.6f seconds\n" "Root delay : %.9f seconds\n"
"Root dispersion : %.6f seconds\n" "Root dispersion : %.9f seconds\n"
"Update interval : %.1f seconds\n" "Update interval : %.1f seconds\n"
"Leap status : %L\n", "Leap status : %L\n",
(unsigned long)ref_id, name, (unsigned long)ref_id, name,
@@ -2262,7 +2262,7 @@ process_cmd_ntpdata(char *line)
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
if (specified_addr) { if (specified_addr) {
if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) { if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname"); LOG(LOGS_ERR, "Could not get address for hostname");
return 0; return 0;
} }
} else { } else {
@@ -2301,7 +2301,7 @@ process_cmd_ntpdata(char *line)
"Precision : %d (%.9f seconds)\n" "Precision : %d (%.9f seconds)\n"
"Root delay : %.6f seconds\n" "Root delay : %.6f seconds\n"
"Root dispersion : %.6f seconds\n" "Root dispersion : %.6f seconds\n"
"Reference ID : %R\n" "Reference ID : %R (%s)\n"
"Reference time : %T\n" "Reference time : %T\n"
"Offset : %+.9f seconds\n" "Offset : %+.9f seconds\n"
"Peer delay : %.9f seconds\n" "Peer delay : %.9f seconds\n"
@@ -2325,7 +2325,10 @@ process_cmd_ntpdata(char *line)
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision), reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay), UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion), UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
(unsigned long)ntohl(reply.data.ntp_data.ref_id), &ref_time, (unsigned long)ntohl(reply.data.ntp_data.ref_id),
reply.data.ntp_data.stratum <= 1 ?
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
&ref_time,
UTI_FloatNetworkToHost(reply.data.ntp_data.offset), UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay), UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion), UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion),
@@ -2416,7 +2419,7 @@ process_cmd_smoothtime(CMD_Request *msg, const char *line)
} else if (!strcmp(line, "activate")) { } else if (!strcmp(line, "activate")) {
msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE); msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE);
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for smoothtime command"); LOG(LOGS_ERR, "Invalid syntax for smoothtime command");
return 0; return 0;
} }
@@ -2564,7 +2567,7 @@ process_cmd_manual_delete(CMD_Request *msg, const char *line)
int index; int index;
if (sscanf(line, "%d", &index) != 1) { if (sscanf(line, "%d", &index) != 1) {
LOG(LOGS_ERR, LOGF_Client, "Bad syntax for manual delete command"); LOG(LOGS_ERR, "Bad syntax for manual delete command");
return 0; return 0;
} }
@@ -2582,7 +2585,6 @@ process_cmd_settime(char *line)
time_t now, new_time; time_t now, new_time;
CMD_Request request; CMD_Request request;
CMD_Reply reply; CMD_Reply reply;
long offset_cs;
double dfreq_ppm, new_afreq_ppm; double dfreq_ppm, new_afreq_ppm;
double offset; double offset;
@@ -2596,9 +2598,8 @@ process_cmd_settime(char *line)
ts.tv_nsec = 0; ts.tv_nsec = 0;
UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts); UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts);
request.command = htons(REQ_SETTIME); request.command = htons(REQ_SETTIME);
if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP, 1)) { if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP2, 1)) {
offset_cs = ntohl(reply.data.manual_timestamp.centiseconds); offset = UTI_FloatNetworkToHost(reply.data.manual_timestamp.offset);
offset = 0.01 * (double)(int32_t)offset_cs;
dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm); dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm);
new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm); new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm);
printf("Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm\n", printf("Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm\n",
@@ -2627,7 +2628,7 @@ process_cmd_makestep(CMD_Request *msg, char *line)
if (*line) { if (*line) {
if (sscanf(line, "%lf %d", &threshold, &limit) != 2) { if (sscanf(line, "%lf %d", &threshold, &limit) != 2) {
LOG(LOGS_ERR, LOGF_Client, "Bad syntax for makestep command"); LOG(LOGS_ERR, "Bad syntax for makestep command");
return 0; return 0;
} }
msg->command = htons(REQ_MODIFY_MAKESTEP); msg->command = htons(REQ_MODIFY_MAKESTEP);
@@ -2720,7 +2721,8 @@ process_cmd_waitsync(char *line)
max_skew_ppm = 0.0; max_skew_ppm = 0.0;
interval = 10.0; interval = 10.0;
sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval); if (sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval))
;
/* Don't allow shorter interval than 0.1 seconds */ /* Don't allow shorter interval than 0.1 seconds */
if (interval < 0.1) if (interval < 0.1)
@@ -2775,7 +2777,7 @@ process_cmd_dns(const char *line)
} else if (!strcmp(line, "+n")) { } else if (!strcmp(line, "+n")) {
no_dns = 0; no_dns = 0;
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Unrecognized dns command"); LOG(LOGS_ERR, "Unrecognized dns command");
return 0; return 0;
} }
return 1; return 1;
@@ -2790,7 +2792,7 @@ process_cmd_timeout(const char *line)
timeout = atoi(line); timeout = atoi(line);
if (timeout < 100) { if (timeout < 100) {
LOG(LOGS_ERR, LOGF_Client, "Timeout %d is too short", timeout); LOG(LOGS_ERR, "Timeout %d is too short", timeout);
return 0; return 0;
} }
initial_timeout = timeout; initial_timeout = timeout;
@@ -2805,8 +2807,8 @@ process_cmd_retries(const char *line)
int retries; int retries;
retries = atoi(line); retries = atoi(line);
if (retries < 0) { if (retries < 0 || retries > 30) {
LOG(LOGS_ERR, LOGF_Client, "Invalid maximum number of retries"); LOG(LOGS_ERR, "Invalid maximum number of retries");
return 0; return 0;
} }
max_retries = retries; max_retries = retries;
@@ -2828,11 +2830,12 @@ process_cmd_keygen(char *line)
snprintf(hash_name, sizeof (hash_name), "MD5"); snprintf(hash_name, sizeof (hash_name), "MD5");
#endif #endif
sscanf(line, "%u %16s %d", &id, hash_name, &bits); if (sscanf(line, "%u %16s %d", &id, hash_name, &bits))
;
length = CLAMP(10, (bits + 7) / 8, sizeof (key)); length = CLAMP(10, (bits + 7) / 8, sizeof (key));
if (HSH_GetHashId(hash_name) < 0) { if (HSH_GetHashId(hash_name) < 0) {
LOG(LOGS_ERR, LOGF_Client, "Unknown hash function %s", hash_name); LOG(LOGS_ERR, "Unknown hash function %s", hash_name);
return 0; return 0;
} }
@@ -3024,11 +3027,11 @@ process_line(char *line)
} else if (!strcmp(command, "authhash") || } else if (!strcmp(command, "authhash") ||
!strcmp(command, "password")) { !strcmp(command, "password")) {
/* Warn, but don't return error to not break scripts */ /* Warn, but don't return error to not break scripts */
LOG(LOGS_WARN, LOGF_Client, "Authentication is no longer supported"); LOG(LOGS_WARN, "Authentication is no longer supported");
do_normal_submit = 0; do_normal_submit = 0;
ret = 1; ret = 1;
} else { } else {
LOG(LOGS_ERR, LOGF_Client, "Unrecognized command"); LOG(LOGS_ERR, "Unrecognized command");
do_normal_submit = 0; do_normal_submit = 0;
} }
@@ -3045,7 +3048,7 @@ process_line(char *line)
static int static int
process_args(int argc, char **argv, int multi) process_args(int argc, char **argv, int multi)
{ {
int total_length, i, ret; int total_length, i, ret = 0;
char *line; char *line;
total_length = 0; total_length = 0;
@@ -3091,7 +3094,7 @@ static void
display_gpl(void) display_gpl(void)
{ {
printf("chrony version %s\n" printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2016 Richard P. Curnow and others\n" "Copyright (C) 1997-2003, 2007, 2009-2017 Richard P. Curnow and others\n"
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n" "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n" "you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n", "GNU General Public License version 2 for details.\n\n",
@@ -3100,54 +3103,80 @@ display_gpl(void)
/* ================================================== */ /* ================================================== */
static void
print_help(const char *progname)
{
printf("Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]\n",
progname);
}
/* ================================================== */
static void
print_version(void)
{
printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
}
/* ================================================== */
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
char *line; char *line;
const char *progname = argv[0]; const char *progname = argv[0];
const char *hostnames = NULL; const char *hostnames = NULL;
int ret = 1, multi = 0, family = IPADDR_UNSPEC; int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
int port = DEFAULT_CANDM_PORT; int port = DEFAULT_CANDM_PORT;
/* Parse command line options */ /* Parse (undocumented) long command-line options */
while (++argv, --argc) { for (optind = 1; optind < argc; optind++) {
if (!strcmp(*argv, "-h")) { if (!strcmp("--help", argv[optind])) {
++argv, --argc; print_help(progname);
if (*argv) {
hostnames = *argv;
}
} else if (!strcmp(*argv, "-p")) {
++argv, --argc;
if (*argv) {
port = atoi(*argv);
}
} else if (!strcmp(*argv, "-f")) {
++argv, --argc;
/* For compatibility */
} else if (!strcmp(*argv, "-a")) {
/* For compatibility */
} else if (!strcmp(*argv, "-c")) {
csv_mode = 1;
} else if (!strcmp(*argv, "-d")) {
log_debug_enabled = 1;
} else if (!strcmp(*argv, "-m")) {
multi = 1;
} else if (!strcmp(*argv, "-n")) {
no_dns = 1;
} else if (!strcmp(*argv, "-4")) {
family = IPADDR_INET4;
} else if (!strcmp(*argv, "-6")) {
family = IPADDR_INET6;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
return 0; return 0;
} else if (!strncmp(*argv, "-", 1)) { } else if (!strcmp("--version", argv[optind])) {
LOG(LOGS_ERR, LOGF_Client, print_version();
"Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]", return 0;
progname); }
}
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "46acdf:h:mnp:v")) != -1) {
switch (opt) {
case '4':
case '6':
family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
break;
case 'a':
case 'f':
/* For compatibility only */
break;
case 'c':
csv_mode = 1;
break;
case 'd':
log_debug_enabled = 1;
break;
case 'h':
hostnames = optarg;
break;
case 'm':
multi = 1;
break;
case 'n':
no_dns = 1;
break;
case 'p':
port = atoi(optarg);
break;
case 'v':
print_version();
return 0;
default:
print_help(progname);
return 1; return 1;
} else {
break; /* And process remainder of line as a command */
} }
} }
@@ -3155,7 +3184,7 @@ main(int argc, char **argv)
on_terminal = 1; on_terminal = 1;
} }
if (on_terminal && (argc == 0)) { if (on_terminal && optind == argc) {
display_gpl(); display_gpl();
} }
@@ -3170,10 +3199,10 @@ main(int argc, char **argv)
sockaddrs = get_sockaddrs(hostnames, port); sockaddrs = get_sockaddrs(hostnames, port);
if (!open_io()) if (!open_io())
LOG_FATAL(LOGF_Client, "Could not open connection to daemon"); LOG_FATAL("Could not open connection to daemon");
if (argc > 0) { if (optind < argc) {
ret = process_args(argc, argv, multi); ret = process_args(argc - optind, argv + optind, multi);
} else { } else {
do { do {
line = read_line(); line = read_line();

View File

@@ -119,7 +119,7 @@ static int cmd_token_shift;
prevent an attacker sending requests with spoofed source address prevent an attacker sending requests with spoofed source address
from blocking responses to the address completely. */ from blocking responses to the address completely. */
#define MIN_LEAK_RATE 0 #define MIN_LEAK_RATE 1
#define MAX_LEAK_RATE 4 #define MAX_LEAK_RATE 4
static int ntp_leak_rate; static int ntp_leak_rate;
@@ -128,6 +128,9 @@ static int cmd_leak_rate;
/* Flag indicating whether the last response was dropped */ /* Flag indicating whether the last response was dropped */
#define FLAG_NTP_DROPPED 0x1 #define FLAG_NTP_DROPPED 0x1
/* NTP limit interval in log2 */
static int ntp_limit_interval;
/* Flag indicating whether facility is turned on or not */ /* Flag indicating whether facility is turned on or not */
static int active; static int active;
@@ -294,7 +297,7 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift); *tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
*max_tokens = *tokens_per_packet * burst; *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); *max_tokens, *tokens_per_packet, *token_shift);
} }
@@ -305,19 +308,31 @@ CLG_Initialise(void)
{ {
int interval, burst, leak_rate; int interval, burst, leak_rate;
CNF_GetNTPRateLimit(&interval, &burst, &leak_rate); max_ntp_tokens = max_cmd_tokens = 0;
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, set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
&ntp_token_shift); &ntp_token_shift);
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE); ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
}
CNF_GetCommandRateLimit(&interval, &burst, &leak_rate); if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) {
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet, set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet,
&cmd_token_shift); &cmd_token_shift);
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE); cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
}
active = !CNF_GetNoClientLog(); active = !CNF_GetNoClientLog();
if (!active) if (!active) {
if (ntp_leak_rate || cmd_leak_rate)
LOG_FATAL("ratelimit cannot be used with noclientlog");
return; return;
}
/* Calculate the maximum number of slots that can be allocated in the /* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash configured memory limit. Take into account expanding of the hash
@@ -460,7 +475,7 @@ CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
record->flags & FLAG_NTP_DROPPED ? record->flags & FLAG_NTP_DROPPED ?
&record->ntp_timeout_rate : &record->ntp_rate); &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_hits, record->ntp_rate, record->ntp_timeout_rate,
record->ntp_tokens); record->ntp_tokens);
@@ -484,7 +499,7 @@ CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift, &record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
&record->cmd_rate); &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); record->cmd_hits, record->cmd_rate, record->cmd_tokens);
return get_index(record); return get_index(record);
@@ -520,7 +535,7 @@ CLG_LimitNTPResponseRate(int index)
Record *record; Record *record;
int drop; int drop;
if (!ntp_leak_rate) if (!ntp_tokens_per_packet)
return 0; return 0;
record = ARR_GetElement(records, index); record = ARR_GetElement(records, index);
@@ -561,7 +576,7 @@ CLG_LimitCommandResponseRate(int index)
{ {
Record *record; Record *record;
if (!cmd_leak_rate) if (!cmd_tokens_per_packet)
return 0; return 0;
record = ARR_GetElement(records, index); record = ARR_GetElement(records, index);
@@ -596,6 +611,14 @@ void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
/* ================================================== */ /* ================================================== */
int
CLG_GetNtpMinPoll(void)
{
return ntp_limit_interval;
}
/* ================================================== */
int int
CLG_GetNumberOfIndices(void) CLG_GetNumberOfIndices(void)
{ {

View File

@@ -39,6 +39,7 @@ extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index); extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index); extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts); 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. */ /* And some reporting functions, for use by chronyc. */

View File

@@ -161,7 +161,7 @@ prepare_socket(int family, int port_number)
sock_fd = socket(family, SOCK_DGRAM, 0); sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 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)); UTI_SockaddrFamilyToString(family), strerror(errno));
return -1; return -1;
} }
@@ -172,14 +172,14 @@ prepare_socket(int family, int port_number)
if (family != AF_UNIX) { if (family != AF_UNIX) {
/* Allow reuse of port number */ /* Allow reuse of port number */
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) { 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 */ /* Don't quit - we might survive anyway */
} }
#ifdef IP_FREEBIND #ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */ /* Allow binding to address that doesn't exist yet */
if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) { 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 #endif
@@ -188,7 +188,7 @@ prepare_socket(int family, int port_number)
#ifdef IPV6_V6ONLY #ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */ /* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) { 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 #endif
} }
@@ -230,7 +230,7 @@ prepare_socket(int family, int port_number)
my_addr.un.sun_family = family; my_addr.un.sun_family = family;
if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s", if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s",
CNF_GetBindCommandPath()) >= sizeof (my_addr.un.sun_path)) 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); unlink(my_addr.un.sun_path);
break; break;
default: default:
@@ -238,7 +238,7 @@ prepare_socket(int family, int port_number)
} }
if (bind(sock_fd, &my_addr.sa, my_addr_len) < 0) { 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)); UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd); close(sock_fd);
return -1; return -1;
@@ -315,7 +315,7 @@ CAM_Initialise(int family)
&& sock_fd6 < 0 && sock_fd6 < 0
#endif #endif
) { ) {
LOG_FATAL(LOGF_CmdMon, "Could not open any command socket"); LOG_FATAL("Could not open any command socket");
} }
access_auth_table = ADF_CreateTable(); access_auth_table = ADF_CreateTable();
@@ -396,12 +396,12 @@ transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to)
&where_to->sa, addrlen); &where_to->sa, addrlen);
if (status < 0) { 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)); UTI_SockaddrToString(&where_to->sa), sock_fd, strerror(errno));
return; 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); UTI_SockaddrToString(&where_to->sa), sock_fd);
} }
@@ -568,14 +568,13 @@ static void
handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message) handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
struct timespec ts; struct timespec ts;
long offset_cs; double offset, dfreq_ppm, new_afreq_ppm;
double dfreq_ppm, new_afreq_ppm;
UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts); UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts);
if (!MNL_IsEnabled()) { if (!MNL_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED); tx_message->status = htons(STT_NOTENABLED);
} else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) { } else if (MNL_AcceptTimestamp(&ts, &offset, &dfreq_ppm, &new_afreq_ppm)) {
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP); tx_message->reply = htons(RPY_MANUAL_TIMESTAMP2);
tx_message->data.manual_timestamp.centiseconds = htonl((int32_t)offset_cs); 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.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm);
tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm); tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm);
} else { } else {
@@ -874,7 +873,7 @@ handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
double dfreq; double dfreq;
dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq); dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq);
LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6); 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);
} }
/* ================================================== */ /* ================================================== */
@@ -887,7 +886,7 @@ handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
sec = (int32_t)ntohl(rx_message->data.doffset.sec); sec = (int32_t)ntohl(rx_message->data.doffset.sec);
usec = (int32_t)ntohl(rx_message->data.doffset.usec); usec = (int32_t)ntohl(rx_message->data.doffset.usec);
doffset = (double) sec + 1.0e-6 * (double) 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); LCL_AccumulateOffset(doffset, 0.0);
} }
@@ -1258,14 +1257,14 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
&where_from.sa, &from_length); &where_from.sa, &from_length);
if (status < 0) { 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); strerror(errno), sock_fd);
return; return;
} }
if (from_length > sizeof (where_from) || if (from_length > sizeof (where_from) ||
from_length <= sizeof (where_from.sa.sa_family)) { 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; return;
} }
@@ -1300,7 +1299,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
assert(0); 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); status, UTI_SockaddrToString(&where_from.sa), sock_fd);
if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) { if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) {
@@ -1319,7 +1318,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* We don't know how to process anything like this or an error reply /* We don't know how to process anything like this or an error reply
would be larger than the request */ would be larger than the request */
DEBUG_LOG(LOGF_CmdMon, "Command packet dropped"); DEBUG_LOG("Command packet dropped");
return; return;
} }
@@ -1341,7 +1340,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
tx_message.pad5 = 0; tx_message.pad5 = 0;
if (rx_message.version != PROTO_VERSION_NUMBER) { 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); rx_message.version, PROTO_VERSION_NUMBER);
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) { if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
@@ -1353,7 +1352,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
if (rx_command >= N_REQUEST_TYPES || if (rx_command >= N_REQUEST_TYPES ||
expected_length < (int)offsetof(CMD_Request, data)) { 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); tx_message.status = htons(STT_INVALID);
transmit_reply(&tx_message, &where_from); transmit_reply(&tx_message, &where_from);
@@ -1361,7 +1360,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
} }
if (read_length < expected_length) { 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); expected_length);
tx_message.status = htons(STT_BADPKTLENGTH); tx_message.status = htons(STT_BADPKTLENGTH);
@@ -1376,7 +1375,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* Don't reply to all requests from hosts other than localhost if the rate /* Don't reply to all requests from hosts other than localhost if the rate
is excessive */ is excessive */
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) { 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; return;
} }
@@ -1627,7 +1626,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
break; break;
default: default:
DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command); DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED); tx_message.status = htons(STT_FAILED);
break; break;
} }

189
conf.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -66,7 +66,8 @@ static void parse_log(char *);
static void parse_mailonchange(char *); static void parse_mailonchange(char *);
static void parse_makestep(char *); static void parse_makestep(char *);
static void parse_maxchange(char *); static void parse_maxchange(char *);
static void parse_ratelimit(char *line, int *interval, int *burst, int *leak); static void parse_ratelimit(char *line, int *enabled, int *interval,
int *burst, int *leak);
static void parse_refclock(char *); static void parse_refclock(char *);
static void parse_smoothtime(char *); static void parse_smoothtime(char *);
static void parse_source(char *line, NTP_Source_Type type, int pool); static void parse_source(char *line, NTP_Source_Type type, int pool);
@@ -78,7 +79,7 @@ static void parse_tempcomp(char *);
static int restarted = 0; static int restarted = 0;
static char *rtc_device; static char *rtc_device;
static int acquisition_port = -1; static int acquisition_port = -1;
static int ntp_port = 123; static int ntp_port = NTP_PORT;
static char *keys_file = NULL; static char *keys_file = NULL;
static char *drift_file = NULL; static char *drift_file = NULL;
static char *rtc_file = NULL; static char *rtc_file = NULL;
@@ -96,13 +97,13 @@ static double combine_limit = 3.0;
static int cmd_port = DEFAULT_CANDM_PORT; static int cmd_port = DEFAULT_CANDM_PORT;
static int raw_measurements = 0;
static int do_log_measurements = 0; static int do_log_measurements = 0;
static int do_log_statistics = 0; static int do_log_statistics = 0;
static int do_log_tracking = 0; static int do_log_tracking = 0;
static int do_log_rtc = 0; static int do_log_rtc = 0;
static int do_log_refclocks = 0; static int do_log_refclocks = 0;
static int do_log_tempcomp = 0; static int do_log_tempcomp = 0;
static int do_dump_on_exit = 0;
static int log_banner = 32; static int log_banner = 32;
static char *logdir; static char *logdir;
static char *dumpdir; static char *dumpdir;
@@ -190,12 +191,14 @@ static char *ntp_signd_socket = NULL;
static char *pidfile; static char *pidfile;
/* Rate limiting parameters */ /* Rate limiting parameters */
static int ntp_ratelimit_interval = -10; static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_burst = 16; static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 2; static int ntp_ratelimit_leak = 2;
static int cmd_ratelimit_interval = -10; static int cmd_ratelimit_enabled = 0;
static int cmd_ratelimit_burst = 16; static int cmd_ratelimit_interval = -4;
static int cmd_ratelimit_leak = 0; static int cmd_ratelimit_burst = 8;
static int cmd_ratelimit_leak = 2;
/* Smoothing constants */ /* Smoothing constants */
static double smooth_max_freq = 0.0; /* in ppm */ static double smooth_max_freq = 0.0; /* in ppm */
@@ -220,7 +223,7 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */ /* Name of the user to which will be dropped root privileges. */
static char *user; static char *user;
/* Array of strings for interfaces with HW timestamping */ /* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces; static ARR_Instance hwts_interfaces;
typedef struct { typedef struct {
@@ -267,7 +270,7 @@ static const char *processed_command;
static void static void
command_parse_error(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_command, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : ""); processed_file ? processed_file : "");
} }
@@ -277,7 +280,7 @@ command_parse_error(void)
static void static void
other_parse_error(const char *message) 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 " : "", message, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : ""); processed_file ? processed_file : "");
} }
@@ -310,7 +313,7 @@ check_number_of_args(char *line, int num)
num -= get_number_of_args(line); num -= get_number_of_args(line);
if (num) { 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", num > 0 ? "Missing" : "Too many",
processed_command, line_number, processed_file ? " in file " : "", processed_command, line_number, processed_file ? " in file " : "",
processed_file ? processed_file : ""); processed_file ? processed_file : "");
@@ -320,11 +323,11 @@ check_number_of_args(char *line, int num)
/* ================================================== */ /* ================================================== */
void void
CNF_Initialise(int r) CNF_Initialise(int r, int client_only)
{ {
restarted = r; restarted = r;
hwts_interfaces = ARR_CreateInstance(sizeof (char *)); hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
init_sources = ARR_CreateInstance(sizeof (IPAddr)); init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source)); ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
@@ -336,11 +339,18 @@ CNF_Initialise(int r)
dumpdir = Strdup(""); dumpdir = Strdup("");
logdir = Strdup(""); logdir = Strdup("");
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup(DEFAULT_PID_FILE);
rtc_device = Strdup(DEFAULT_RTC_DEVICE); rtc_device = Strdup(DEFAULT_RTC_DEVICE);
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE); hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER); 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);
}
} }
/* ================================================== */ /* ================================================== */
@@ -351,7 +361,7 @@ CNF_Finalise(void)
unsigned int i; unsigned int i;
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++) for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
Free(*(char **)ARR_GetElement(hwts_interfaces, i)); Free(((CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, i))->name);
ARR_DestroyInstance(hwts_interfaces); ARR_DestroyInstance(hwts_interfaces);
for (i = 0; i < ARR_GetSize(ntp_sources); i++) for (i = 0; i < ARR_GetSize(ntp_sources); i++)
@@ -394,12 +404,12 @@ CNF_ReadFile(const char *filename)
in = fopen(filename, "r"); in = fopen(filename, "r");
if (!in) { 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)); filename, strerror(errno));
return; return;
} }
DEBUG_LOG(LOGF_Configure, "Reading %s", filename); DEBUG_LOG("Reading %s", filename);
for (i = 1; fgets(line, sizeof(line), in); i++) { for (i = 1; fgets(line, sizeof(line), in); i++) {
CNF_ParseLine(filename, i, line); CNF_ParseLine(filename, i, line);
@@ -452,7 +462,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "cmdport")) { } else if (!strcasecmp(command, "cmdport")) {
parse_int(p, &cmd_port); parse_int(p, &cmd_port);
} else if (!strcasecmp(command, "cmdratelimit")) { } else if (!strcasecmp(command, "cmdratelimit")) {
parse_ratelimit(p, &cmd_ratelimit_interval, &cmd_ratelimit_burst, &cmd_ratelimit_leak); parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
&cmd_ratelimit_burst, &cmd_ratelimit_leak);
} else if (!strcasecmp(command, "combinelimit")) { } else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit); parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "corrtimeratio")) { } else if (!strcasecmp(command, "corrtimeratio")) {
@@ -464,7 +475,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "dumpdir")) { } else if (!strcasecmp(command, "dumpdir")) {
parse_string(p, &dumpdir); parse_string(p, &dumpdir);
} else if (!strcasecmp(command, "dumponexit")) { } else if (!strcasecmp(command, "dumponexit")) {
do_dump_on_exit = parse_null(p); /* Silently ignored */
} else if (!strcasecmp(command, "fallbackdrift")) { } else if (!strcasecmp(command, "fallbackdrift")) {
parse_fallbackdrift(p); parse_fallbackdrift(p);
} else if (!strcasecmp(command, "hwclockfile")) { } else if (!strcasecmp(command, "hwclockfile")) {
@@ -532,7 +543,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "port")) { } else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port); parse_int(p, &ntp_port);
} else if (!strcasecmp(command, "ratelimit")) { } else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_interval, &ntp_ratelimit_burst, &ntp_ratelimit_leak); parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
} else if (!strcasecmp(command, "refclock")) { } else if (!strcasecmp(command, "refclock")) {
parse_refclock(p); parse_refclock(p);
} else if (!strcasecmp(command, "reselectdist")) { } else if (!strcasecmp(command, "reselectdist")) {
@@ -563,7 +575,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
!strcasecmp(command, "generatecommandkey") || !strcasecmp(command, "generatecommandkey") ||
!strcasecmp(command, "linux_freq_scale") || !strcasecmp(command, "linux_freq_scale") ||
!strcasecmp(command, "linux_hz")) { !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 { } else {
other_parse_error("Invalid command"); other_parse_error("Invalid command");
} }
@@ -637,11 +649,13 @@ parse_source(char *line, NTP_Source_Type type, int pool)
/* ================================================== */ /* ================================================== */
static void static void
parse_ratelimit(char *line, int *interval, int *burst, int *leak) parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
{ {
int n, val; int n, val;
char *opt; char *opt;
*enabled = 1;
while (*line) { while (*line) {
opt = line; opt = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
@@ -667,9 +681,9 @@ static void
parse_refclock(char *line) parse_refclock(char *line)
{ {
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options; int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int max_lock_age; int max_lock_age, pps_forced;
uint32_t ref_id, lock_ref_id; 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; char *p, *cmd, *name, *param;
unsigned char ref[5]; unsigned char ref[5];
RefclockParameters *refclock; RefclockParameters *refclock;
@@ -677,6 +691,7 @@ parse_refclock(char *line)
poll = 4; poll = 4;
dpoll = 0; dpoll = 0;
filter_length = 64; filter_length = 64;
pps_forced = 0;
pps_rate = 0; pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES; min_samples = SRC_DEFAULT_MINSAMPLES;
max_samples = SRC_DEFAULT_MAXSAMPLES; max_samples = SRC_DEFAULT_MAXSAMPLES;
@@ -685,6 +700,7 @@ parse_refclock(char *line)
delay = 1e-9; delay = 1e-9;
precision = 0.0; precision = 0.0;
max_dispersion = 0.0; max_dispersion = 0.0;
pulse_width = 0.0;
ref_id = 0; ref_id = 0;
max_lock_age = 2; max_lock_age = 2;
lock_ref_id = 0; lock_ref_id = 0;
@@ -749,12 +765,18 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "delay")) { } else if (!strcasecmp(cmd, "delay")) {
if (sscanf(line, "%lf%n", &delay, &n) != 1) if (sscanf(line, "%lf%n", &delay, &n) != 1)
break; break;
} else if (!strcasecmp(cmd, "pps")) {
n = 0;
pps_forced = 1;
} else if (!strcasecmp(cmd, "precision")) { } else if (!strcasecmp(cmd, "precision")) {
if (sscanf(line, "%lf%n", &precision, &n) != 1) if (sscanf(line, "%lf%n", &precision, &n) != 1)
break; break;
} else if (!strcasecmp(cmd, "maxdispersion")) { } else if (!strcasecmp(cmd, "maxdispersion")) {
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1) if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
break; break;
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
} else if (!strcasecmp(cmd, "noselect")) { } else if (!strcasecmp(cmd, "noselect")) {
n = 0; n = 0;
sel_options |= SRC_SELECT_NOSELECT; sel_options |= SRC_SELECT_NOSELECT;
@@ -784,6 +806,7 @@ parse_refclock(char *line)
refclock->driver_poll = dpoll; refclock->driver_poll = dpoll;
refclock->poll = poll; refclock->poll = poll;
refclock->filter_length = filter_length; refclock->filter_length = filter_length;
refclock->pps_forced = pps_forced;
refclock->pps_rate = pps_rate; refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples; refclock->min_samples = min_samples;
refclock->max_samples = max_samples; refclock->max_samples = max_samples;
@@ -792,6 +815,7 @@ parse_refclock(char *line)
refclock->delay = delay; refclock->delay = delay;
refclock->precision = precision; refclock->precision = precision;
refclock->max_dispersion = max_dispersion; refclock->max_dispersion = max_dispersion;
refclock->pulse_width = pulse_width;
refclock->ref_id = ref_id; refclock->ref_id = ref_id;
refclock->max_lock_age = max_lock_age; refclock->max_lock_age = max_lock_age;
refclock->lock_ref_id = lock_ref_id; refclock->lock_ref_id = lock_ref_id;
@@ -807,7 +831,10 @@ parse_log(char *line)
log_name = line; log_name = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (*log_name) { 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; do_log_measurements = 1;
} else if (!strcmp(log_name, "statistics")) { } else if (!strcmp(log_name, "statistics")) {
do_log_statistics = 1; do_log_statistics = 1;
@@ -867,7 +894,7 @@ parse_initstepslew(char *line)
if (DNS_Name2IPAddress(hostname, &ip_addr, 1) == DNS_Success) { if (DNS_Name2IPAddress(hostname, &ip_addr, 1) == DNS_Success) {
ARR_AppendElement(init_sources, &ip_addr); ARR_AppendElement(init_sources, &ip_addr);
} else { } 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);
} }
} }
} }
@@ -1044,7 +1071,7 @@ parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
} }
} else { } 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 = (AllowDeny *)ARR_GetNewElement(restrictions);
new_node->allow = allow; new_node->allow = allow;
new_node->all = all; new_node->all = all;
@@ -1159,7 +1186,7 @@ parse_broadcast(char *line)
} }
} else { } else {
/* default port */ /* default port */
port = 123; port = NTP_PORT;
} }
destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts); destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts);
@@ -1238,8 +1265,63 @@ parse_tempcomp(char *line)
static void static void
parse_hwtimestamp(char *line) parse_hwtimestamp(char *line)
{ {
check_number_of_args(line, 1); CNF_HwTsInterface *iface;
*(char **)ARR_GetNewElement(hwts_interfaces) = Strdup(line); 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();
} }
/* ================================================== */ /* ================================================== */
@@ -1249,11 +1331,15 @@ parse_include(char *line)
{ {
glob_t gl; glob_t gl;
size_t i; size_t i;
int r;
check_number_of_args(line, 1); check_number_of_args(line, 1);
if (glob(line, 0, NULL, &gl)) { if ((r = glob(line, GLOB_ERR | GLOB_NOMAGIC, NULL, &gl)) != 0) {
DEBUG_LOG(LOGF_Configure, "glob of %s failed", line); if (r != GLOB_NOMATCH)
LOG_FATAL("Could not search for files matching %s", line);
DEBUG_LOG("glob of %s failed", line);
return; return;
} }
@@ -1279,7 +1365,7 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
existed. It MUST NOT be accessible by others as permissions on Unix existed. It MUST NOT be accessible by others as permissions on Unix
domain sockets are ignored on some systems (e.g. Solaris). */ domain sockets are ignored on some systems (e.g. Solaris). */
if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) { 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'; bind_cmd_path[0] = '\0';
} }
@@ -1418,8 +1504,9 @@ CNF_GetDumpDir(void)
/* ================================================== */ /* ================================================== */
int int
CNF_GetLogMeasurements(void) CNF_GetLogMeasurements(int *raw)
{ {
*raw = raw_measurements;
return do_log_measurements; return do_log_measurements;
} }
@@ -1497,14 +1584,6 @@ CNF_GetRtcDevice(void)
/* ================================================== */ /* ================================================== */
int
CNF_GetDumpOnExit(void)
{
return do_dump_on_exit;
}
/* ================================================== */
double double
CNF_GetMaxUpdateSkew(void) CNF_GetMaxUpdateSkew(void)
{ {
@@ -1685,7 +1764,7 @@ CNF_SetupAccessRestrictions(void)
node = ARR_GetElement(ntp_restrictions, i); node = ARR_GetElement(ntp_restrictions, i);
status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
if (!status) { 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);
} }
} }
@@ -1693,7 +1772,7 @@ CNF_SetupAccessRestrictions(void)
node = ARR_GetElement(cmd_restrictions, i); node = ARR_GetElement(cmd_restrictions, i);
status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
if (!status) { 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);
} }
} }
@@ -1823,20 +1902,22 @@ CNF_GetLockMemory(void)
/* ================================================== */ /* ================================================== */
void CNF_GetNTPRateLimit(int *interval, int *burst, int *leak) int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
{ {
*interval = ntp_ratelimit_interval; *interval = ntp_ratelimit_interval;
*burst = ntp_ratelimit_burst; *burst = ntp_ratelimit_burst;
*leak = ntp_ratelimit_leak; *leak = ntp_ratelimit_leak;
return ntp_ratelimit_enabled;
} }
/* ================================================== */ /* ================================================== */
void CNF_GetCommandRateLimit(int *interval, int *burst, int *leak) int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak)
{ {
*interval = cmd_ratelimit_interval; *interval = cmd_ratelimit_interval;
*burst = cmd_ratelimit_burst; *burst = cmd_ratelimit_burst;
*leak = cmd_ratelimit_leak; *leak = cmd_ratelimit_leak;
return cmd_ratelimit_enabled;
} }
/* ================================================== */ /* ================================================== */
@@ -1921,8 +2002,12 @@ CNF_GetInitStepThreshold(void)
/* ================================================== */ /* ================================================== */
ARR_Instance int
CNF_GetHwTsInterfaces(void) CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
{ {
return hwts_interfaces; if (index >= ARR_GetSize(hwts_interfaces))
return 0;
*iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index);
return 1;
} }

26
conf.h
View File

@@ -29,10 +29,9 @@
#define GOT_CONF_H #define GOT_CONF_H
#include "addressing.h" #include "addressing.h"
#include "array.h"
#include "reference.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 void CNF_Finalise(void);
extern char *CNF_GetRtcDevice(void); extern char *CNF_GetRtcDevice(void);
@@ -53,7 +52,7 @@ extern char *CNF_GetDriftFile(void);
extern char *CNF_GetLogDir(void); extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void); extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(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_GetLogStatistics(void);
extern int CNF_GetLogTracking(void); extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void); extern int CNF_GetLogRtc(void);
@@ -61,7 +60,6 @@ extern int CNF_GetLogRefclocks(void);
extern int CNF_GetLogTempComp(void); extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void); extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void); extern char *CNF_GetRtcFile(void);
extern int CNF_GetDumpOnExit(void);
extern int CNF_GetManualEnabled(void); extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void); extern int CNF_GetCommandPort(void);
extern int CNF_GetRtcOnUtc(void); extern int CNF_GetRtcOnUtc(void);
@@ -102,8 +100,8 @@ extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void); extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void); extern int CNF_GetLockMemory(void);
extern void CNF_GetNTPRateLimit(int *interval, int *burst, int *leak); extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetCommandRateLimit(int *interval, int *burst, int *leak); extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only); extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2); extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
@@ -120,6 +118,20 @@ extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void); extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void); extern double CNF_GetInitStepThreshold(void);
extern ARR_Instance CNF_GetHwTsInterfaces(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 */ #endif /* GOT_CONF_H */

62
configure vendored
View File

@@ -4,7 +4,8 @@
# chronyd/chronyc - Programs for keeping computer clocks accurate. # chronyd/chronyc - Programs for keeping computer clocks accurate.
# #
# Copyright (C) Richard P. Curnow 1997-2003 # 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
# #
# ======================================================================= # =======================================================================
@@ -427,6 +428,15 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME SETTIME BINDSOCKET" priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
fi fi
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 ")" echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
;; ;;
SunOS) SunOS)
@@ -489,14 +499,16 @@ MYCPPFLAGS="$CPPFLAGS"
MYLDFLAGS="$LDFLAGS" MYLDFLAGS="$LDFLAGS"
if [ "x$MYCC" = "x" ]; then if [ "x$MYCC" = "x" ]; then
MYCC=gcc for cc in gcc clang cc ""; do
if ! test_code "$MYCC" '' '' '' ''; then if [ "x$cc" = "x" ]; then
MYCC=cc
if ! test_code "$MYCC" '' '' '' ''; then
echo "error: no C compiler found" echo "error: no C compiler found"
exit 1 exit 1
fi fi
MYCC=$cc
if test_code "$MYCC" '' '' '' ''; then
break
fi fi
done
else else
if ! test_code "$MYCC" '' '' '' ''; then if ! test_code "$MYCC" '' '' '' ''; then
echo "error: C compiler $MYCC cannot create executables" echo "error: C compiler $MYCC cannot create executables"
@@ -506,9 +518,25 @@ fi
if [ "x$MYCFLAGS" = "x" ]; then if [ "x$MYCFLAGS" = "x" ]; then
MYCFLAGS="-O2 -g" 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 fi
if [ "x$MYCC" = "xgcc" ]; then if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall" MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi fi
@@ -630,6 +658,11 @@ if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; the
add_def HAVE_ARC4RANDOM add_def HAVE_ARC4RANDOM
fi fi
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then
add_def HAVE_GETRANDOM
fi
RECVMMSG_CODE=' RECVMMSG_CODE='
struct mmsghdr hdr; struct mmsghdr hdr;
return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);' return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
@@ -657,6 +690,17 @@ if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
then then
add_def HAVE_LINUX_TIMESTAMPING add_def HAVE_LINUX_TIMESTAMPING
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o" 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 fi
timepps_h="" timepps_h=""
@@ -708,7 +752,7 @@ then
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper # NAME2IPADDRESS shouldn't be enabled with other operations as the helper
# process works on one request at the time and the async resolver could # process works on one request at the time and the async resolver could
# block the main thread # block the main thread
priv_ops="NAME2IPADDRESS" priv_ops="NAME2IPADDRESS RELOADDNS"
EXTRA_LIBS="$EXTRA_LIBS -lseccomp" EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi fi
@@ -731,8 +775,10 @@ fi
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \ if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \ grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \ 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 then
grep 'HAVE_LINUX_TIMESTAMPING' config.h > /dev/null ||
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o"
add_def FEAT_PHC add_def FEAT_PHC
fi fi

View File

@@ -1,7 +1,8 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // 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 // 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 // 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 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. 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 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_::: *maxpoll* _poll_:::
In a similar way, the user might want to constrain the maximum polling interval. 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 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*::: *iburst*:::
If this option is set, the interval between the first four polls will be 2 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 seconds instead of _minpoll_. This is useful to quickly get the first update of
@@ -105,7 +110,7 @@ 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* measurement to be ignored, this level can be defined with the *maxdelay*
option. For example, *maxdelay 0.3* would indicate that measurements with a 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 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_::: *maxdelayratio* _ratio_:::
This option is similar to the maxdelay option above. *chronyd* keeps a record 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 of the minimum round-trip delay amongst the previous measurements that it has
@@ -160,9 +165,8 @@ synchronisation only if they agree with the trusted and required source.
*xleave*::: *xleave*:::
This option enables an interleaved mode which allows the server or the peer to 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 send transmit timestamps captured after the actual transmission (e.g. when the
server or the peer is running *chronyd* with HW timestamping enabled by the server or the peer is running *chronyd* with software (kernel) or hardware
<<hwtimestamp,*hwtimestamp*>> directive). This can significantly improve the timestamping). This can significantly improve the accuracy of the measurements.
accuracy of the measurements.
+ +
The interleaved mode is compatible with servers that support only the basic The interleaved mode is compatible with servers that support only the basic
mode, but peers must both support and have enabled the interleaved mode, mode, but peers must both support and have enabled the interleaved mode,
@@ -172,9 +176,12 @@ 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. and the state might be dropped when there are too many clients (e.g.
<<clientloglimit,*clientloglimit*>> is too small), or it might be overwritten <<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 by other clients that have the same IP address (e.g. computers behind NAT or
someone sending requests with a spoofed source address). The *presend* option someone sending requests with a spoofed source address).
can be used to shorten the interval in which the server has to keep the state +
for this computer and be able to respond in the interleaved mode. 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_::: *polltarget* _target_:::
Target number of measurements to use for the regression algorithm which Target number of measurements to use for the regression algorithm which
*chronyd* will try to maintain by adjusting the polling interval between *chronyd* will try to maintain by adjusting the polling interval between
@@ -337,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 started in the boot sequence after *chronyd* from reading the clock before it
has been stepped. 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 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 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*::: *PPS*:::
Driver for the kernel PPS (pulse per second) API. The parameter is the path to 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 the PPS device (typically _/dev/pps?_). As PPS refclocks do not supply full
synchronisation by default. String *:clear* can be appended to the path to use time, another time source (e.g. NTP server or non-PPS refclock) is needed to
the clear events instead. 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 *clear*::::
another time source (NTP or non-PPS refclock) in order to complete samples from By default, the PPS refclock uses assert events (rising edge) for
the PPS refclock. An alternative is to enable the <<local,*local*>> directive synchronisation. With this option, it will use clear events (falling edge)
to allow synchronisation with some unknown but constant offset. instead.
For example: +
:::
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 SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
---- ----
+ +
*SHM*::: *SHM*:::
NTP shared memory driver. This driver uses a shared memory segment to receive 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 samples from another process (e.g. *gpsd*). The parameter is the number of the
segment, typically 0, 1, 2 or 3. For example: 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*::: *SOCK*:::
Unix domain socket driver. It is similar to the SHM driver, but samples are 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 received from a Unix domain socket instead of shared memory and the messages
@@ -397,17 +413,44 @@ refclock SOCK /var/run/chrony.ttyS0.sock
+ +
*PHC*::: *PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of 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 the PTP clock which should be used as a time source. If the clock is kept in
http://linuxptp.sourceforge.net[*linuxptp*]. PTP clocks are typically kept in TAI instead of UTC (e.g. it is synchronised by a PTP daemon), the current
TAI instead of UTC, so the *offset* option should be used to compensate for the UTC-TAI offset needs to be specified by the *offset* option. Alternatively, the
current UTC-TAI offset. For example: *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_::: *poll* _poll_:::
Timestamps produced by refclock drivers are not used immediately, but they are Timestamps produced by refclock drivers are not used immediately, but they are
@@ -440,6 +483,17 @@ 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. 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 Increasing this value is useful when the samples are produced at a lower rate
than the pulses. The default is 2. 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_::: *offset* _offset_:::
This option can be used to compensate for a constant error. The specified 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. offset (in seconds) is applied to all samples produced by the reference clock.
@@ -451,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 source selection or to make it prefer other sources. The default is 1e-9 (1
nanosecond). nanosecond).
*precision* _precision_::: *precision* _precision_:::
This option sets the refclock precision (in seconds). The default is 1e-6 (1 This option sets the precision of the reference clock (in seconds). The default
microsecond) for SHM refclock, and 1e-9 (1 nanosecond) for SOCK, PPS and PHC value is the estimated precision of the system clock.
refclocks.
*maxdispersion* _dispersion_::: *maxdispersion* _dispersion_:::
Maximum allowed dispersion for filtered samples (in seconds). Samples with Maximum allowed dispersion for filtered samples (in seconds). Samples with
larger estimated dispersion are ignored. By default, this limit is disabled. larger estimated dispersion are ignored. By default, this limit is disabled.
@@ -534,19 +587,18 @@ can be specified.
To compute the rate of gain or loss of time, *chronyd* has to store a 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. measurement history for each of the time sources it uses.
+ +
Certain systems (Linux, FreeBSD, NetBSD, Solaris) have operating system support All supported systems, with the exception of macOS 10.12 and earlier, have
for setting the rate of gain or loss to compensate for known errors. (On Mac OS operating system support for setting the rate of gain or loss to compensate for
X, *chronyd* must simulate such a capability by periodically slewing the system known errors.
clock forwards or backwards by a suitable amount to compensate for the error (On macOS 10.12 and earlier, *chronyd* must simulate such a capability by
built up since the previous slew.) 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 For such systems, it is possible to save the measurement history across
restarts of *chronyd* (assuming no changes are made to the system clock 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 behaviour whilst it is not running). The *dumpdir* directive defines the
*dumponexit* directive in the configuration file, or the directory where the measurement histories are saved when *chronyd* exits,
<<chronyc.adoc#dump,*dump*>> command in *chronyc*), the *dumpdir* directive or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
should be used to define the directory where the measurement histories are
saved.
+ +
An example of the directive is: An example of the directive is:
+ +
@@ -558,11 +610,6 @@ 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 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_. to files named by their reference ID in form of _refid:XXXXXXXX.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.)
[[maxsamples]]*maxsamples* _samples_:: [[maxsamples]]*maxsamples* _samples_::
The *maxsamples* directive sets the default maximum number of samples that The *maxsamples* directive sets the default maximum number of samples that
*chronyd* should keep for each source. This setting can be overridden for *chronyd* should keep for each source. This setting can be overridden for
@@ -644,8 +691,9 @@ distances are in milliseconds.
[[corrtimeratio]]*corrtimeratio* _ratio_:: [[corrtimeratio]]*corrtimeratio* _ratio_::
When *chronyd* is slewing the system clock to correct an offset, the rate at 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, which it is slewing adds to the frequency error of the clock. On all supported
FreeBSD, NetBSD and Solaris this rate can be controlled. 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 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 clock is slewed for an average correction according to the source history and
@@ -728,8 +776,8 @@ that error is corrected. There are four options:
When inserting a leap second, the kernel steps the system clock backwards by 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 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 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, default mode when the system driver supports leap seconds (i.e. all supported
FreeBSD, NetBSD and Solaris). systems with the exception of macOS 12 and earlier).
*step*::: *step*:::
This is similar to the *system* mode, except the clock is stepped by 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 *chronyd* instead of the kernel. It can be useful to avoid bugs in the kernel
@@ -784,15 +832,23 @@ clients to safely synchronise with multiple identically configured leap
smearing servers. smearing servers.
[[leapsectz]]*leapsectz* _timezone_:: [[leapsectz]]*leapsectz* _timezone_::
This directive is used to set the name of the timezone in the system tz This directive specifies a timezone in the system tz database which *chronyd*
database which *chronyd* can use to find out when will the next leap second can use to determine when will the next leap second occur and what is the
occur. It will periodically check if the times 23:59:59 and 23:59:60 are valid current offset between TAI and UTC. It will periodically check if 23:59:59 and
on Jun 30 and Dec 31 in the timezone. This typically works with the *right/UTC* 23:59:60 are valid times in the timezone. This typically works with the
timezone. _right/UTC_ timezone.
+ +
This directive is mainly useful with reference clocks which do not provide When a leap second is announced, the timezone needs to be updated at least 12
leap second information. It is not necessary to restart *chronyd* if the tz hours before the leap second. It is not necessary to restart *chronyd*.
database is updated with a new leap second at least 12 hours before the event. +
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: An example of the directive is:
+ +
@@ -864,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 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 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 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 By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
limited by the system driver rather than this directive. limited by the system driver rather than this directive.
@@ -899,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 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 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. 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 For each system there is a maximum frequency offset of the clock that can be set
can be set by the driver. On Linux it is 100000 ppm, on FreeBSD and NetBSD by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
it is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
limitation, setting *maxslewrate* on FreeBSD and NetBSD to a value between 500 setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
ppm and 5000 ppm will effectively set it to 500 ppm. 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). By default, the maximum slew rate is set to 83333.333 ppm (one twelfth).
[[tempcomp]] [[tempcomp]]
@@ -994,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: Examples of the use of the directive are as follows:
+ +
---- ----
allow foo.example.net allow 1.2.3.4
allow 1.2 allow 1.2
allow 3.4.5 allow 3.4.5
allow 6.7.8/22 allow 6.7.8/22
@@ -1005,7 +1065,8 @@ allow ::/0
allow 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_ 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, (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_ the third directive allows any node with an IPv4 address of the form _3.4.5.x_
@@ -1046,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 it is of greater use for reconfiguration at run-time via *chronyc* with the
<<chronyc.adoc#allow,*allow all*>> command. <<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 Note, if the <<initstepslew,*initstepslew*>> directive is used in the
configuration file, each of the computers listed in that directive must allow configuration file, each of the computers listed in that directive must allow
client access by this computer for it to work. client access by this computer for it to work.
@@ -1221,8 +1286,8 @@ source port used in NTP client requests can be set by the
<<acquisitionport,*acquisitionport*>> directive. <<acquisitionport,*acquisitionport*>> directive.
[[ratelimit]]*ratelimit* [_option_]...:: [[ratelimit]]*ratelimit* [_option_]...::
This directive configures response rate limiting for NTP packets. Its purpose This directive enables response rate limiting for NTP packets. Its purpose is
is to reduce network traffic with misconfigured or broken NTP clients that are to reduce network traffic with misconfigured or broken NTP clients that are
polling the server too frequently. The limits are applied to individual IP polling the server too frequently. The limits are applied to individual IP
addresses. If multiple clients share one IP address (e.g. multiple hosts behind addresses. If multiple clients share one IP address (e.g. multiple hosts behind
NAT), the sum of their traffic will be limited. If a client that increases its NAT), the sum of their traffic will be limited. If a client that increases its
@@ -1237,16 +1302,16 @@ in any order):
+ +
*interval*::: *interval*:::
This option sets the minimum interval between responses. It is defined as a This option sets the minimum interval between responses. It is defined as a
power of 2 in seconds. The default value is -10 (1/1024 of a second, or 1024 power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
packets per second). The minimum value is -19 (524288 packets per second) and is -19 (524288 packets per second) and the maximum value is 12 (one packet per
the maximum value is 12 (one packet per 4096 seconds). Note that with values 4096 seconds). Note that with values below -4 the rate limiting is coarse
below -4 the rate limiting is coarse (responses are allowed in bursts, even if (responses are allowed in bursts, even if the interval between them is shorter
the interval between them is shorter than the specified interval). than the specified interval).
*burst*::: *burst*:::
This option sets the maximum number of responses that can be sent in a 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 temporarily exceeding the limit specified by the *interval* option. This is
useful for clients that make rapid measurements on start (e.g. *chronyd* with useful for clients that make rapid measurements on start (e.g. *chronyd* with
the *iburst* option). The default value is 16. The minimum value is 1 and the the *iburst* option). The default value is 8. The minimum value is 1 and the
maximum value is 255. maximum value is 255.
*leak*::: *leak*:::
This option sets the rate at which responses are randomly allowed even if the This option sets the rate at which responses are randomly allowed even if the
@@ -1254,23 +1319,19 @@ limits specified by the *interval* and *burst* options are exceeded. This is
necessary to prevent an attacker who is sending requests with a spoofed necessary to prevent an attacker who is sending requests with a spoofed
source address from completely blocking responses to that address. The leak source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at 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 0, which least every fourth request has a response. The minimum value is 1 and the
disables the rate limiting, and the maximum value is 4 (one response per 16 maximum value is 4.
requests).
:: ::
+ +
An example use of the directive is: An example use of the directive is:
+ +
---- ----
ratelimit interval 1 burst 8 ratelimit interval 1 burst 16
---- ----
+ +
This would reduce the response rate for IP addresses sending packets on average 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 8 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). packets, by up to 75% (with default *leak* of 2).
+
Rate limiting can be disabled by setting the *leak* option to 0, or by the
<<noclientlog,*noclientlog*>> directive.
[[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]:: [[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
The *smoothtime* directive can be used to enable smoothing of the time that The *smoothtime* directive can be used to enable smoothing of the time that
@@ -1397,18 +1458,16 @@ This would make *chronyd* use UDP 257 as its command port. (*chronyc* would
need to be run with the *-p 257* switch to inter-operate correctly.) need to be run with the *-p 257* switch to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...:: [[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive is identical to the <<ratelimit,*ratelimit*>> directive, except This directive enables response rate limiting for command packets. It is
it configures rate limiting for command packets and responses to localhost are similar to the <<ratelimit,*ratelimit*>> directive, except responses to
never limited. It is disabled by default (the default *leak* is 0). localhost are never limited and the default interval is -4 (16 packets per
second).
+ +
An example of the use of the directive is: An example of the use of the directive is:
+ +
---- ----
cmdratelimit interval -2 burst 128 leak 2 cmdratelimit interval 2
---- ----
+
This would reduce response rate for addresses that send more than 4 requests
per second, or bursts of more than 128 packets, by up to 75%.
=== Real-time clock (RTC) === Real-time clock (RTC)
@@ -1511,10 +1570,12 @@ 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 directive. A banner is periodically written to the files to indicate the
meanings of the columns. meanings of the columns.
+ +
*measurements*::: *rawmeasurements*:::
This option logs the raw NTP measurements and related information to a file 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 called _measurements.log_. An entry is made for each packet received from the
line in the file) from the log file is shown below. 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.
+ +
---- ----
2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \ 2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
@@ -1556,6 +1617,12 @@ from the example line above):
. Source of the local receive timestamp . Source of the local receive timestamp
(_D_=daemon, _K_=kernel, _H_=hardware). [K] (_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*::: *statistics*:::
This option logs information about the regression processing to a file called This option logs information about the regression processing to a file called
_statistics.log_. An example line (which actually appears as a single line in _statistics.log_. An example line (which actually appears as a single line in
@@ -1782,7 +1849,7 @@ sendmail binary.
=== Miscellaneous === Miscellaneous
[[hwtimestamp]]*hwtimestamp* _interface_:: [[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
This directive enables hardware timestamping of NTP packets sent to and This directive enables hardware timestamping of NTP packets sent to and
received from the specified network interface. The network interface controller received from the specified network interface. The network interface controller
(NIC) uses its own clock to accurately timestamp the actual transmissions and (NIC) uses its own clock to accurately timestamp the actual transmissions and
@@ -1799,24 +1866,64 @@ 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 timestamping, which can be verified with the *ethtool -T* command. The list of
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_, capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
_SOF_TIMESTAMPING_TX_HARDWARE_, _SOF_TIMESTAMPING_RX_HARDWARE_, and the filter _SOF_TIMESTAMPING_TX_HARDWARE_, _SOF_TIMESTAMPING_RX_HARDWARE_, and the filter
modes should have _HWTSTAMP_FILTER_ALL_. When *chronyd* is running, no other modes should include _HWTSTAMP_FILTER_ALL_ or _HWTSTAMP_FILTER_NTP_ALL_. When
process should be working with the clock on the NIC. If no *hwtimestamp* *chronyd* is running, no other process (e.g. a PTP daemon) should be working
directive is specified, *chronyd* will try to use software (kernel) with the NIC clock.
timestamping. With both hardware and software timestamping there are +
some limitations on which packets can be actually timestamped, e.g. transmit 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 timestamping does not currently work with IPv6 packets using IP options and
hardware receive timestamping does not work with packets from bridged hardware timestamping of packets received from bridges, bonds, and other
interfaces. The timestamping used in measurements is indicated in the 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, _measurements.log_ file if enabled by the <<log,*log measurements*>> directive,
and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in *chronyc*. and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in *chronyc*.
+ +
If the specified interface is _*_, *chronyd* will try to enable HW timestamping If the specified interface is _*_, *chronyd* will try to enable HW timestamping
on all available interfaces. on all available interfaces.
+ +
An example of the directive is: 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 eth0
hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
---- ----
[[include]]*include* _pattern_:: [[include]]*include* _pattern_::
@@ -2136,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. RTC gains or loses time relative to true time.
When the computer is powered down, the measurement histories for all the NTP When the computer is powered down, the measurement histories for all the NTP
servers are saved to files (if the <<dumponexit,*dumponexit*>> directive is servers are saved to files, and the RTC tracking information is also
specified in the configuration file), and the RTC tracking information is also
saved to a file (if the <<rtcfile,*rtcfile*>> directive has been specified). 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*>> These pieces of information are also saved if the <<chronyc.adoc#dump,*dump*>>
and <<chronyc.adoc#writertc,*writertc*>> commands respectively are issued and <<chronyc.adoc#writertc,*writertc*>> commands respectively are issued
@@ -2190,7 +2296,6 @@ log statistics measurements tracking
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
makestep 1.0 3 makestep 1.0 3
maxupdateskew 100.0 maxupdateskew 100.0
dumponexit
dumpdir @CHRONYVARDIR@ dumpdir @CHRONYVARDIR@
rtcfile @CHRONYVARDIR@/rtc rtcfile @CHRONYVARDIR@/rtc
---- ----
@@ -2225,27 +2330,36 @@ information to be saved.
*chronyd* can be configured to operate as a public NTP server, e.g. to join the *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 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 is similar to the NTP client with permanent connection, except it needs to
allow client access from all addresses. It is recommended to handpick at least allow client access from all addresses. It is recommended to find at least four
few good servers, and possibly combine them with a random selection of other good servers (e.g. from the pool, or on the NTP homepage). If the server has a
servers in the pool. The rate limiting interval can be increased to save more hardware reference clock (e.g. a GPS receiver), it can be specified by the
bandwidth on misconfigured and broken NTP clients. The *-r* option with the <<refclock,*refclock*>> directive.
*dumpdir* directive shortens the time for which *chronyd* will not serve time
to its clients when it needs to be restarted for any reason.
The configuration file might be: 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 foo.example.net iburst
server bar.example.net iburst server bar.example.net iburst
server baz.example.net iburst server baz.example.net iburst
pool pool.ntp.org iburst server qux.example.net iburst
makestep 1.0 3 makestep 1.0 3
rtcsync rtcsync
allow allow
ratelimit interval 1 clientloglimit 100000000
leapsectz right/UTC
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
dumpdir @CHRONYRUNDIR@ dumpdir @CHRONYRUNDIR@
dumponexit
---- ----
== SEE ALSO == SEE ALSO

View File

@@ -1,6 +1,7 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2016 // Copyright (C) Miroslav Lichvar 2009-2016
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
@@ -129,15 +130,15 @@ performance. An example of the output is shown below.
---- ----
Reference ID : CB00710F (foo.example.net) Reference ID : CB00710F (foo.example.net)
Stratum : 3 Stratum : 3
Ref time (UTC) : Fri Feb 3 15:00:29 2012 Ref time (UTC) : Fri Jan 27 09:49:17 2017
System time : 0.000001501 seconds slow of NTP time System time : 0.000006523 seconds slow of NTP time
Last offset : -0.000001632 seconds Last offset : -0.000006747 seconds
RMS offset : 0.000002360 seconds RMS offset : 0.000035822 seconds
Frequency : 331.898 ppm fast Frequency : 3.225 ppm slow
Residual freq : 0.004 ppm Residual freq : -0.000 ppm
Skew : 0.154 ppm Skew : 0.129 ppm
Root delay : 0.373169 seconds Root delay : 0.013639022 seconds
Root dispersion : 0.024780 seconds Root dispersion : 0.001100737 seconds
Update interval : 64.2 seconds Update interval : 64.2 seconds
Leap status : Normal Leap status : Normal
---- ----
@@ -186,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 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 has advanced 1 second, it has actually advanced by 1.000001 seconds relative to
true time. 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*::: *Residual freq*:::
This shows the '`residual frequency`' for the currently selected reference This shows the '`residual frequency`' for the currently selected reference
source. This reflects any difference between what the measurements from the source. This reflects any difference between what the measurements from the
@@ -458,7 +456,7 @@ Poll interval : 10 (1024 seconds)
Precision : -24 (0.000000060 seconds) Precision : -24 (0.000000060 seconds)
Root delay : 0.000015 seconds Root delay : 0.000015 seconds
Root dispersion : 0.000015 seconds Root dispersion : 0.000015 seconds
Reference ID : 50505331 Reference ID : 47505300 (GPS)
Reference time : Fri Nov 25 15:22:12 2016 Reference time : Fri Nov 25 15:22:12 2016
Offset : -0.000060878 seconds Offset : -0.000060878 seconds
Peer delay : 0.000175634 seconds Peer delay : 0.000175634 seconds
@@ -1120,17 +1118,15 @@ purged. An example of how to do this is shown below.
[[dump]]*dump*:: [[dump]]*dump*::
The *dump* command causes *chronyd* to write its current history of 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 measurements for each of its sources to dump files in the directory specified
support the *-r* option when *chronyd* is restarted. in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
+ directive. Note that *chronyd* does this automatically when it exits. This
The *dump* command is somewhat equivalent to the command is mainly useful for inspection of the history whilst *chronyd* is
<<chrony.conf.adoc#dumponexit,*dumponexit*>> directive in the configuration running.
file.
+ [[rekey]]*rekey*::
To use the *dump* command, you might want to configure the name of the The *rekey* command causes *chronyd* to re-read the key file specified in the
directory into which the dump files will be written. This can only be configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
done in the configuration file with the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive.
=== Client commands === Client commands

View File

@@ -62,17 +62,22 @@ When run in this mode, the program will not detach itself from the terminal.
*-d*:: *-d*::
When run in this mode, the program will not detach itself from the terminal, 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 *chronyd* was compiled with debugging support, this option can be used twice to
print also debugging messages. print also debugging messages.
*-l* _file_::
This option specifies a file which should be used for logging instead of syslog
or terminal.
*-q*:: *-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal. will not detach from the terminal.
*-Q*:: *-Q*::
This option is similar to *-q*, but it will only print the offset without any This option is similar to the *-q* option, except it only prints the offset
corrections of the clock. without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
*-r*:: *-r*::
This option will try to reload and then delete files containing sample This option will try to reload and then delete files containing sample
@@ -82,8 +87,8 @@ histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
directive in the configuration file. This option is useful if you want to stop 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. 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 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 compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
and Solaris). Solaris, and macOS 10.13 or later).
*-R*:: *-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>> When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -151,6 +156,13 @@ support this option.
This option will lock *chronyd* into RAM so that it will never be paged out. This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux. 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*:: *-v*::
With this option *chronyd* will print version number to the terminal and exit. With this option *chronyd* will print version number to the terminal and exit.

View File

@@ -79,7 +79,7 @@ rtcsync
You need to add an `allow` directive to the _chrony.conf_ file in order to open 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 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? === 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 No. Starting from version 1.25, `chronyd` will keep trying to resolve
the names specified by the `server`, `pool`, and `peer` directives in an the names specified by the `server`, `pool`, and `peer` directives in an
increasing interval until it succeeds. The `online` command can be issued from 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? === How can I make `chronyd` more secure?
@@ -161,7 +161,7 @@ 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 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 default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 8 (samples) for `polltarget`. The default values should be used `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 have 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 permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system shorter polling intervals may significantly improve the accuracy of the system
clock. clock.
@@ -195,15 +195,27 @@ server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
---- ----
If your server supports the interleaved mode, the `xleave` option should be If your server supports the interleaved mode, the `xleave` option should be
added to the `server` directive in order to receive server's more accurate added to the `server` directive in order to allow the server to send the
hardware or kernel transmit timestamps. When combined with local hardware client more accurate hardware or kernel transmit timestamps. When combined with
timestamping, a sub-microsecond accuracy may be possible. An example could be local hardware timestamping, sub-microsecond accuracy may be possible. An
example could be
---- ----
server ntp.local minpoll 2 maxpoll 2 xleave server ntp.local minpoll 2 maxpoll 2 xleave
hwtimestamp eth0 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? === What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the They were removed in version 2.2. Authentication is no longer supported in the
@@ -242,8 +254,17 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
=== Are NTP servers specified with the `offline` option? === Are NTP servers specified with the `offline` option?
Check that you're using ``chronyc``'s `online` and `offline` commands Check that you're using ``chronyc``'s `online` and `offline` commands
appropriately. Again, check in _measurements.log_ to see if you're getting any appropriately. The `activity` command prints the number of sources that are
data back from the server. 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? === Is `chronyd` allowed to step the system clock?
@@ -396,15 +417,15 @@ 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? === 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. `chronyd` will keep trying to access the sources that it thinks are online, and
When the network is connected again, it will take some time (on average half of it will take longer before new measurements are actually made and the clock is
the maximum polling interval) before new measurements are made and the clock is corrected when the network is connected again. If the sources were set to
corrected. If the servers were set to offline and the `online` command was offline, `chronyd` would make new measurements immediately after issuing the
issued when the network was connected, `chronyd` would make new measurements `online` command.
immediately.
The `auto_offline` option to the `server` entry in the _chrony.conf_ file may Unless the network connection lasts only few minutes (less than the maximum
be useful to switch the servers to the offline state automatically. polling interval), the delay is usually not a problem, and it may be acceptable
to keep all sources online all the time.
== Operating systems == Operating systems

View File

@@ -1,5 +1,6 @@
[Unit] [Unit]
Description=Wait for chrony to synchronize system clock Description=Wait for chrony to synchronize system clock
Documentation=man:chronyc(1)
After=chronyd.service After=chronyd.service
Requires=chronyd.service Requires=chronyd.service
Before=time-sync.target Before=time-sync.target

View File

@@ -4,8 +4,8 @@ pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time. # Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew # Allow the system clock to be stepped in the first three updates
# if the adjustment is larger than 1 second. # if its offset is larger than 1 second.
makestep 1.0 3 makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC). # 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. # Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew # Allow the system clock to be stepped in the first three updates
# if the adjustment is larger than 1 second. # if its offset is larger than 1 second.
makestep 1.0 3 makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC). # Enable kernel synchronization of the real-time clock (RTC).
rtcsync rtcsync
# Allow NTP client access from local network. # Enable hardware timestamping on all interfaces that support it.
#allow 192.168/16 #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 #local stratum 10
# Specify file containing keys for NTP authentication. # Specify file containing keys for NTP authentication.

View File

@@ -33,42 +33,30 @@
! pool pool.ntp.org iburst ! 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 ### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK
# #
# To avoid changes being made to your computer's gain/loss compensation # To avoid changes being made to your computer's gain/loss compensation
# when the measurement history is too erratic, you might want to enable # 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 # one of the following lines. The first seems good with servers on the
# other high-latency connections like slow leased lines), the second # Internet, the second seems OK for a LAN environment.
# seems OK for a LAN environment.
! maxupdateskew 100 ! maxupdateskew 100
! maxupdateskew 5 ! 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 ### FILENAMES ETC
# Chrony likes to keep information about your computer's clock in files. # 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' # machine accesses it. The information can be accessed by the 'clients'
# command of chronyc. You can disable this facility by uncommenting the # command of chronyc. You can disable this facility by uncommenting the
# following line. This will save a bit of memory if you have many # 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 ! noclientlog
# The clientlog size is limited to 512KB by default. If you have many # The clientlog size is limited to 512KB by default. If you have many
# clients, especially in many different subnets, you might want to # clients, you might want to increase the limit.
# increase the limit.
! clientloglimit 4194304 ! clientloglimit 4194304
@@ -196,7 +183,7 @@ driftfile /var/lib/chrony/drift
# clients that are sending requests too frequently, uncomment and edit # clients that are sending requests too frequently, uncomment and edit
# the following line. # the following line.
! limitrate interval 3 burst 8 ! ratelimit interval 3 burst 8
####################################################################### #######################################################################
### REPORTING BIG CLOCK CHANGES ### REPORTING BIG CLOCK CHANGES
@@ -243,7 +230,17 @@ driftfile /var/lib/chrony/drift
# Rate limiting can be enabled also for command packets. (Note, # Rate limiting can be enabled also for command packets. (Note,
# commands from localhost are never limited.) # 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 ### REAL TIME CLOCK
@@ -274,6 +271,12 @@ driftfile /var/lib/chrony/drift
! rtcdevice /dev/misc/rtc ! 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 ### REAL TIME SCHEDULER
# This directive tells chronyd to use the real-time FIFO scheduler with the # This directive tells chronyd to use the real-time FIFO scheduler with the

View File

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

View File

@@ -39,9 +39,6 @@
/* Maximum number of samples per clock */ /* Maximum number of samples per clock */
#define MAX_SAMPLES 16 #define MAX_SAMPLES 16
/* Minimum interval between samples (in seconds) */
#define MIN_SAMPLE_SEPARATION 1.0
struct HCL_Instance_Record { struct HCL_Instance_Record {
/* HW and local reference timestamp */ /* HW and local reference timestamp */
struct timespec hw_ref; struct timespec hw_ref;
@@ -55,6 +52,12 @@ struct HCL_Instance_Record {
/* Number of samples */ /* Number of samples */
int n_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 */ /* Flag indicating the offset and frequency values are valid */
int valid_coefs; int valid_coefs;
@@ -83,7 +86,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */ /* ================================================== */
HCL_Instance HCL_Instance
HCL_CreateInstance(void) HCL_CreateInstance(double min_separation)
{ {
HCL_Instance clock; HCL_Instance clock;
@@ -92,6 +95,7 @@ HCL_CreateInstance(void)
clock->y_data[MAX_SAMPLES - 1] = 0.0; clock->y_data[MAX_SAMPLES - 1] = 0.0;
clock->n_samples = 0; clock->n_samples = 0;
clock->valid_coefs = 0; clock->valid_coefs = 0;
clock->min_separation = min_separation;
LCL_AddParameterChangeHandler(handle_slew, clock); LCL_AddParameterChangeHandler(handle_slew, clock);
@@ -112,7 +116,7 @@ int
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now) HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
{ {
if (!clock->n_samples || if (!clock->n_samples ||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= MIN_SAMPLE_SEPARATION) fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= clock->min_separation)
return 1; return 1;
return 0; return 0;
@@ -137,9 +141,9 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref); hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq; local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
if (hw_delta <= 0.0 || local_delta < MIN_SAMPLE_SEPARATION / 2.0) { if (hw_delta <= 0.0 || local_delta < clock->min_separation / 2.0) {
clock->n_samples = 0; clock->n_samples = 0;
DEBUG_LOG(LOGF_HwClocks, "HW clock reset interval=%f", local_delta); DEBUG_LOG("HW clock reset interval=%f", local_delta);
} }
for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) { for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
@@ -151,16 +155,17 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
clock->n_samples++; clock->n_samples++;
clock->hw_ref = *hw_ts; clock->hw_ref = *hw_ts;
clock->local_ref = *local_ts; clock->local_ref = *local_ts;
clock->last_err = err;
/* Get new coefficients */ /* Get new coefficients */
clock->valid_coefs = clock->valid_coefs =
RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples, RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
clock->y_data + MAX_SAMPLES - clock->n_samples, clock->y_data + MAX_SAMPLES - clock->n_samples,
clock->n_samples, 1.0e-9, &clock->offset, &raw_freq, clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
&n_runs, &best_start); &n_runs, &best_start);
if (!clock->valid_coefs) { if (!clock->valid_coefs) {
DEBUG_LOG(LOGF_HwClocks, "HW clock needs more samples"); DEBUG_LOG("HW clock needs more samples");
return; return;
} }
@@ -172,12 +177,12 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
/* If the fit doesn't cross the error interval of the last sample, throw away /* If the fit doesn't cross the error interval of the last sample, throw away
all previous samples and keep only the frequency estimate */ all previous samples and keep only the frequency estimate */
if (fabs(clock->offset) > err) { if (fabs(clock->offset) > err) {
DEBUG_LOG(LOGF_HwClocks, "HW clock reset offset=%e", clock->offset); DEBUG_LOG("HW clock reset offset=%e", clock->offset);
clock->offset = 0.0; clock->offset = 0.0;
clock->n_samples = 1; clock->n_samples = 1;
} }
DEBUG_LOG(LOGF_HwClocks, "HW clock samples=%d offset=%e freq=%.9e raw_freq=%.9e err=%e ref_diff=%e", 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, clock->n_samples, clock->offset, clock->frequency, raw_freq, err,
UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref)); UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
} }
@@ -196,9 +201,9 @@ HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
offset = clock->offset + elapsed / clock->frequency; offset = clock->offset + elapsed / clock->frequency;
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked); UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
/* Estimation of the error is not implemented yet */ /* Fow now, just return the error of the last sample */
if (err) if (err)
*err = 0.0; *err = clock->last_err;
return 1; return 1;
} }

View File

@@ -29,7 +29,7 @@
typedef struct HCL_Instance_Record *HCL_Instance; typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */ /* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(void); extern HCL_Instance HCL_CreateInstance(double min_separation);
/* Destroy a HW clock instance */ /* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock); extern void HCL_DestroyInstance(HCL_Instance clock);

12
keys.c
View File

@@ -122,7 +122,7 @@ determine_hash_delay(uint32_t key_id)
/* Add on a bit extra to allow for copying, conversions etc */ /* Add on a bit extra to allow for copying, conversions etc */
nsecs = 1.0625e9 * min_diff; nsecs = 1.0625e9 * min_diff;
DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs); DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
return nsecs; return nsecs;
} }
@@ -200,7 +200,7 @@ KEY_Reload(void)
in = fopen(key_file, "r"); in = fopen(key_file, "r");
if (!in) { 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; return;
} }
@@ -212,19 +212,19 @@ KEY_Reload(void)
continue; continue;
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) { 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; continue;
} }
key.hash_id = HSH_GetHashId(hashname); key.hash_id = HSH_GetHashId(hashname);
if (key.hash_id < 0) { 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; continue;
} }
key.len = decode_password(keyval); key.len = decode_password(keyval);
if (!key.len) { 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; continue;
} }
@@ -244,7 +244,7 @@ KEY_Reload(void)
/* Check for duplicates */ /* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) { for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id) 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 */ /* Erase any passwords from stack */

24
local.c
View File

@@ -144,7 +144,9 @@ calculate_sys_precision(void)
best *= 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);
} }
/* ================================================== */ /* ================================================== */
@@ -356,12 +358,12 @@ LCL_ReadRawTime(struct timespec *ts)
{ {
#if HAVE_CLOCK_GETTIME #if HAVE_CLOCK_GETTIME
if (clock_gettime(CLOCK_REALTIME, ts) < 0) if (clock_gettime(CLOCK_REALTIME, ts) < 0)
LOG_FATAL(LOGF_Local, "clock_gettime() failed : %s", strerror(errno)); LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
#else #else
struct timeval tv; struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) if (gettimeofday(&tv, NULL) < 0)
LOG_FATAL(LOGF_Local, "gettimeofday() failed : %s", strerror(errno)); LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
UTI_TimevalToTimespec(&tv, ts); UTI_TimevalToTimespec(&tv, ts);
#endif #endif
@@ -424,7 +426,7 @@ clamp_freq(double freq)
if (freq <= max_freq_ppm && freq >= -max_freq_ppm) if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
return freq; 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); return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
} }
@@ -438,7 +440,7 @@ check_offset(struct timespec *now, double offset)
if (UTI_IsTimeOffsetSane(now, -offset)) if (UTI_IsTimeOffsetSane(now, -offset))
return 1; 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; return 0;
} }
@@ -544,7 +546,7 @@ LCL_ApplyStepOffset(double offset)
return 0; return 0;
if (!(*drv_apply_step_offset)(offset)) { 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; return 0;
} }
@@ -611,7 +613,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
current_freq_ppm = clamp_freq(current_freq_ppm); 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); old_freq_ppm, current_freq_ppm, doffset);
/* Call the system-specific driver for setting the frequency */ /* Call the system-specific driver for setting the frequency */
@@ -658,7 +660,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
current_freq_ppm = (*drv_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);
} }
/* ================================================== */ /* ================================================== */
@@ -682,7 +684,7 @@ LCL_MakeStep(void)
if (!LCL_ApplyStepOffset(-correction)) if (!LCL_ApplyStepOffset(-correction))
return 0; 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; return 1;
} }
@@ -698,10 +700,10 @@ LCL_CanSystemLeap(void)
/* ================================================== */ /* ================================================== */
void void
LCL_SetSystemLeap(int leap) LCL_SetSystemLeap(int leap, int tai_offset)
{ {
if (drv_set_leap) { if (drv_set_leap) {
(drv_set_leap)(leap); (drv_set_leap)(leap, tai_offset);
} }
} }

View File

@@ -201,10 +201,11 @@ extern int LCL_MakeStep(void);
does something */ does something */
extern int LCL_CanSystemLeap(void); extern int LCL_CanSystemLeap(void);
/* Routine to set the system clock to correct itself for a leap second if /* Routine to set the system clock to correct itself for a leap second and also
supported. Leap second will be inserted at the end of the day if the set its TAI-UTC offset. If supported, leap second will be inserted at the
argument is positive, deleted if negative, and zero resets the setting. */ end of the day if the argument is positive, deleted if negative, and zero
extern void LCL_SetSystemLeap(int leap); resets the setting. */
extern void LCL_SetSystemLeap(int leap, int tai_offset);
/* Routine to set a frequency correction (in ppm) that should be applied /* Routine to set a frequency correction (in ppm) that should be applied
to local clock to compensate for temperature changes. A positive to local clock to compensate for temperature changes. A positive

View File

@@ -54,8 +54,8 @@ typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
raw time to get the corrected time */ raw time to get the corrected time */
typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err); typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err);
/* System driver to schedule leap second */ /* System driver to schedule leap seconds and set TAI-UTC offset */
typedef void (*lcl_SetLeapDriver)(int leap); typedef void (*lcl_SetLeapDriver)(int leap, int tai_offset);
/* System driver to set the synchronisation status */ /* System driver to set the synchronisation status */
typedef void (*lcl_SetSyncStatusDriver)(int synchronised, double est_error, double max_error); 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 */ /* Flag indicating we have initialised */
static int initialised = 0; static int initialised = 0;
static FILE *file_log;
static int system_log = 0; static int system_log = 0;
static int parent_fd = 0; static int parent_fd = 0;
@@ -69,6 +70,7 @@ void
LOG_Initialise(void) LOG_Initialise(void)
{ {
initialised = 1; initialised = 1;
file_log = stderr;
} }
/* ================================================== */ /* ================================================== */
@@ -79,6 +81,8 @@ LOG_Finalise(void)
{ {
if (system_log) { if (system_log) {
closelog(); closelog();
} else {
fclose(file_log);
} }
LOG_CycleLogFiles(); 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); syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else { } 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, void LOG_Message(LOG_Severity severity,
#if DEBUG > 0 #if DEBUG > 0
LOG_Facility facility, int line_number, int line_number, const char *filename, const char *function_name,
const char *filename, const char *function_name,
#endif #endif
const char *format, ...) const char *format, ...)
{ {
@@ -136,10 +139,10 @@ void LOG_Message(LOG_Severity severity,
time(&t); time(&t);
stm = *gmtime(&t); stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm); 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 > 0
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION) 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 #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 void
@@ -241,7 +259,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
char filename[512], *logdir = CNF_GetLogDir(); char filename[512], *logdir = CNF_GetLogDir();
if (logdir[0] == '\0') { if (logdir[0] == '\0') {
LOG(LOGS_WARN, LOGF_Logging, "logdir not specified"); LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL; logfiles[id].name = NULL;
return; return;
} }
@@ -249,7 +267,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
if (snprintf(filename, sizeof(filename), "%s/%s.log", if (snprintf(filename, sizeof(filename), "%s/%s.log",
logdir, logfiles[id].name) >= sizeof (filename) || logdir, logfiles[id].name) >= sizeof (filename) ||
!(logfiles[id].file = fopen(filename, "a"))) { !(logfiles[id].file = fopen(filename, "a"))) {
LOG(LOGS_WARN, LOGF_Logging, "Could not open log file %s", filename); LOG(LOGS_WARN, "Could not open log file %s", filename);
logfiles[id].name = NULL; logfiles[id].name = NULL;
return; return;
} }

View File

@@ -46,26 +46,26 @@ extern int log_debug_enabled;
#endif #endif
#if DEBUG > 0 #if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \ #define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__) LOG_Message(severity, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#else #else
#define LOG_MESSAGE(severity, facility, ...) \ #define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __VA_ARGS__) LOG_Message(severity, __VA_ARGS__)
#endif #endif
#define DEBUG_LOG(facility, ...) \ #define DEBUG_LOG(...) \
do { \ do { \
if (DEBUG && log_debug_enabled) \ if (DEBUG && log_debug_enabled) \
LOG_MESSAGE(LOGS_DEBUG, facility, __VA_ARGS__); \ LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
} while (0) } while (0)
#define LOG_FATAL(facility, ...) \ #define LOG_FATAL(...) \
do { \ do { \
LOG_MESSAGE(LOGS_FATAL, facility, __VA_ARGS__); \ LOG_MESSAGE(LOGS_FATAL, __VA_ARGS__); \
exit(1); \ exit(1); \
} while (0) } while (0)
#define LOG(severity, facility, ...) LOG_MESSAGE(severity, facility, __VA_ARGS__) #define LOG(severity, ...) LOG_MESSAGE(severity, __VA_ARGS__)
/* Definition of severity */ /* Definition of severity */
typedef enum { typedef enum {
@@ -76,50 +76,6 @@ typedef enum {
LOGS_DEBUG LOGS_DEBUG
} LOG_Severity; } 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_NtpIOLinux,
LOGF_NtpCore,
LOGF_NtpSignd,
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_HwClocks,
LOGF_Smooth,
} LOG_Facility;
/* Init function */ /* Init function */
extern void LOG_Initialise(void); extern void LOG_Initialise(void);
@@ -128,9 +84,8 @@ extern void LOG_Finalise(void);
/* Line logging function */ /* Line logging function */
#if DEBUG > 0 #if DEBUG > 0
FORMAT_ATTRIBUTE_PRINTF(6, 7) FORMAT_ATTRIBUTE_PRINTF(5, 6)
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility, extern void LOG_Message(LOG_Severity severity, int line_number, const char *filename,
int line_number, const char *filename,
const char *function_name, const char *format, ...); const char *function_name, const char *format, ...);
#else #else
FORMAT_ATTRIBUTE_PRINTF(2, 3) FORMAT_ATTRIBUTE_PRINTF(2, 3)
@@ -144,6 +99,9 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
*/ */
extern void LOG_SetDebugLevel(int level); 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 */ /* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void); extern void LOG_OpenSystemLog(void);

279
main.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -86,6 +86,10 @@ static void
delete_pidfile(void) delete_pidfile(void)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
if (!pidfile[0])
return;
/* Don't care if this fails, there's not a lot we can do */ /* Don't care if this fails, there's not a lot we can do */
unlink(pidfile); unlink(pidfile);
} }
@@ -97,7 +101,7 @@ MAI_CleanupAndExit(void)
{ {
if (!initialised) exit(exit_status); if (!initialised) exit(exit_status);
if (CNF_GetDumpOnExit()) { if (CNF_GetDumpDir()[0] != '\0') {
SRC_DumpSources(); SRC_DumpSources();
} }
@@ -127,9 +131,8 @@ MAI_CleanupAndExit(void)
delete_pidfile(); delete_pidfile();
CNF_Finalise(); CNF_Finalise();
LOG_Finalise();
HSH_Finalise(); HSH_Finalise();
LOG_Finalise();
exit(exit_status); exit(exit_status);
} }
@@ -242,55 +245,45 @@ post_init_rtc_hook(void *anything)
} }
/* ================================================== */ /* ================================================== */
/* Return 1 if the process exists on the system. */
static int static void
does_process_exist(int pid) check_pidfile(void)
{
int status;
status = getsid(pid);
if (status >= 0) {
return 1;
} else {
return 0;
}
}
/* ================================================== */
static int
maybe_another_chronyd_running(int *other_pid)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
FILE *in; FILE *in;
int pid, count; int pid, count;
*other_pid = 0;
in = fopen(pidfile, "r"); in = fopen(pidfile, "r");
if (!in) return 0; if (!in)
return;
count = fscanf(in, "%d", &pid); count = fscanf(in, "%d", &pid);
fclose(in); fclose(in);
if (count != 1) return 0; if (count != 1)
return;
*other_pid = pid; if (getsid(pid) < 0)
return does_process_exist(pid); return;
LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
pid, pidfile);
} }
/* ================================================== */ /* ================================================== */
static void static void
write_lockfile(void) write_pidfile(void)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
FILE *out; FILE *out;
if (!pidfile[0])
return;
out = fopen(pidfile, "w"); out = fopen(pidfile, "w");
if (!out) { 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 { } else {
fprintf(out, "%d\n", (int)getpid()); fprintf(out, "%d\n", (int)getpid());
fclose(out); fclose(out);
@@ -307,14 +300,14 @@ go_daemon(void)
/* Create pipe which will the daemon use to notify the grandparent /* Create pipe which will the daemon use to notify the grandparent
when it's initialised or send an error message */ when it's initialised or send an error message */
if (pipe(pipefd)) { if (pipe(pipefd)) {
LOG_FATAL(LOGF_Main, "Could not detach, pipe failed : %s", strerror(errno)); LOG_FATAL("pipe() failed : %s", strerror(errno));
} }
/* Does this preserve existing signal handlers? */ /* Does this preserve existing signal handlers? */
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno)); LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) { } else if (pid > 0) {
/* In the 'grandparent' */ /* In the 'grandparent' */
char message[1024]; char message[1024];
@@ -325,7 +318,8 @@ go_daemon(void)
if (r) { if (r) {
if (r > 0) { if (r > 0) {
/* Print the error message from the child */ /* Print the error message from the child */
fprintf(stderr, "%.1024s\n", message); message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
} }
exit(1); exit(1);
} else } else
@@ -339,7 +333,7 @@ go_daemon(void)
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno)); LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) { } else if (pid > 0) {
exit(0); /* In the 'parent' */ exit(0); /* In the 'parent' */
} else { } else {
@@ -347,7 +341,7 @@ go_daemon(void)
/* Change current directory to / */ /* Change current directory to / */
if (chdir("/") < 0) { if (chdir("/") < 0) {
LOG_FATAL(LOGF_Main, "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 /* Don't keep stdin/out/err from before. But don't close
@@ -364,142 +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 main
(int argc, char **argv) (int argc, char **argv)
{ {
const char *conf_file = DEFAULT_CONF_FILE; const char *conf_file = DEFAULT_CONF_FILE;
const char *progname = argv[0]; const char *progname = argv[0];
char *user = NULL; char *user = NULL, *log_file = NULL;
struct passwd *pw; struct passwd *pw;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0, timeout = 0; int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
int other_pid;
int scfilter_level = 0, lock_memory = 0, sched_priority = 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; int config_args = 0;
do_platform_checks(); do_platform_checks();
LOG_Initialise(); LOG_Initialise();
/* Parse command line options */ /* Parse (undocumented) long command-line options */
while (++argv, (--argc)>0) { for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) {
if (!strcmp("-f", *argv)) { print_help(progname);
++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);
return 0; return 0;
} else if (!strcmp("-n", *argv)) { } else if (!strcmp("--version", argv[optind])) {
nofork = 1; print_version();
} else if (!strcmp("-d", *argv)) { return 0;
}
}
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++; debug++;
nofork = 1; nofork = 1;
system_log = 0; 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("-t", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &timeout) != 1 || timeout <= 0)
LOG_FATAL(LOGF_Main, "Bad timeout");
} 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] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
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; 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() != 0) { if (getuid() && !client_only)
/* This write to the terminal is OK, it comes before we turn into a daemon */ LOG_FATAL("Not superuser");
fprintf(stderr,"Not superuser\n");
return 1;
}
/* Turn into a daemon */ /* Turn into a daemon */
if (!nofork) { if (!nofork) {
go_daemon(); go_daemon();
} }
if (system_log) { if (log_file) {
LOG_OpenFileLog(log_file);
} else if (system_log) {
LOG_OpenSystemLog(); LOG_OpenSystemLog();
} }
LOG_SetDebugLevel(debug); LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)", LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
CHRONY_VERSION, CHRONYD_FEATURES);
DNS_SetAddressFamily(address_family); DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted); CNF_Initialise(restarted, client_only);
/* Parse the config file or the remaining command line arguments */ /* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
if (!config_args) { if (!config_args) {
CNF_ReadFile(conf_file); CNF_ReadFile(conf_file);
} else { } else {
do { for (; optind < argc; optind++)
CNF_ParseLine(NULL, config_args - argc + 1, *argv); CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
} while (++argv, --argc);
} }
/* Check whether another chronyd may already be running. Do this after /* Check whether another chronyd may already be running */
* forking, so that message logging goes to the right place (i.e. syslog), in check_pidfile();
* 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());
}
/* Write our lockfile to prevent other chronyds running. This has *GOT* to /* Write our pidfile to prevent other chronyds running */
* be done *AFTER* the daemon-creation fork() */ write_pidfile();
write_lockfile();
PRV_Initialise(); PRV_Initialise();
LCL_Initialise(); LCL_Initialise();
SCH_Initialise(); SCH_Initialise();
SYS_Initialise(); SYS_Initialise(clock_control);
RTC_Initialise(do_init_rtc); RTC_Initialise(do_init_rtc);
SRC_Initialise(); SRC_Initialise();
RCL_Initialise(); RCL_Initialise();
@@ -528,13 +551,13 @@ int main
} }
if ((pw = getpwnam(user)) == NULL) 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 */ /* Create all directories before dropping root */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid); CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Drop root privileges if the user has non-zero uid or gid */ /* Drop root privileges if the specified user has a non-zero UID */
if (pw->pw_uid || pw->pw_gid) if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid); SYS_DropRoot(pw->pw_uid, pw->pw_gid);
REF_Initialise(); REF_Initialise();
@@ -563,7 +586,7 @@ int main
REF_SetModeEndHandler(reference_mode_end); REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode); REF_SetMode(ref_mode);
if (timeout) if (timeout > 0)
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL); SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
if (do_init_rtc) { if (do_init_rtc) {
@@ -576,7 +599,7 @@ int main
the scheduler. */ the scheduler. */
SCH_MainLoop(); SCH_MainLoop();
LOG(LOGS_INFO, LOGF_Main, "chronyd exiting"); LOG(LOGS_INFO, "chronyd exiting");
MAI_CleanupAndExit(); MAI_CleanupAndExit();

View File

@@ -97,7 +97,8 @@ MNL_Finalise(void)
/* ================================================== */ /* ================================================== */
static void static void
estimate_and_set_system(struct timespec *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 agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
double b0, b1; double b0, b1;
@@ -108,32 +109,26 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
int found_freq; int found_freq;
double slew_by; double slew_by;
b0 = offset_provided ? offset : 0.0;
b1 = freq = 0.0;
found_freq = 0;
if (n_samples > 1) { if (n_samples > 1) {
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
agos[i] = UTI_DiffTimespecsToDouble(&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; offsets[i] = samples[i].offset;
} }
RGR_FindBestRobustRegression(agos, offsets, n_samples, if (RGR_FindBestRobustRegression(agos, offsets, n_samples, 1.0e-8,
1.0e-8, /* 0.01ppm easily good enough for this! */ &b0, &b1, &n_runs, &best_start)) {
&b0, &b1, &n_runs, &best_start);
/* Ignore b0 from regression; treat offset as being the most /* Ignore b0 from regression; treat offset as being the most
recently entered value. (If the administrator knows he's put recently entered value. (If the administrator knows he's put
an outlier in, he will rerun the settime operation.) However, an outlier in, he will rerun the settime operation.) However,
the frequency estimate comes from the regression. */ the frequency estimate comes from the regression. */
freq = -b1; freq = -b1;
found_freq = 1; found_freq = 1;
} else {
if (offset_provided) {
b0 = offset;
} else {
b0 = 0.0;
} }
b1 = freq = 0.0; } else {
found_freq = 0;
agos[0] = 0.0; agos[0] = 0.0;
offsets[0] = b0; offsets[0] = b0;
} }
@@ -145,21 +140,20 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
} }
if (found_freq) { if (found_freq) {
LOG(LOGS_INFO, LOGF_Manual, LOG(LOGS_INFO, "Making a frequency change of %.3f ppm and a slew of %.6f",
"Making a frequency change of %.3f ppm and a slew of %.6f",
1.0e6 * freq, slew_by); 1.0e6 * freq, slew_by);
REF_SetManualReference(now, REF_SetManualReference(now,
slew_by, slew_by,
freq, skew); freq, skew);
} else { } 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, REF_SetManualReference(now,
slew_by, slew_by,
0.0, skew); 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 (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq;
if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency(); if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency();
@@ -173,7 +167,7 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
/* ================================================== */ /* ================================================== */
int int
MNL_AcceptTimestamp(struct timespec *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 timespec now; struct timespec now;
double offset, diff; double offset, diff;
@@ -210,7 +204,7 @@ MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, dou
samples[n_samples].orig_offset = offset; samples[n_samples].orig_offset = offset;
++n_samples; ++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; return 1;

View File

@@ -33,7 +33,7 @@
extern void MNL_Initialise(void); extern void MNL_Initialise(void);
extern void MNL_Finalise(void); extern void MNL_Finalise(void);
extern int MNL_AcceptTimestamp(struct timespec *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_Enable(void);
extern void MNL_Disable(void); extern void MNL_Disable(void);

View File

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

View File

@@ -78,7 +78,7 @@ end_resolving(int fd, int event, void *anything)
int i; int i;
if (pthread_join(inst->thread, NULL)) { if (pthread_join(inst->thread, NULL)) {
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed"); LOG_FATAL("pthread_join() failed");
} }
resolving_threads--; resolving_threads--;
@@ -110,7 +110,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->status = DNS_Failure; inst->status = DNS_Failure;
if (pipe(inst->pipe)) { if (pipe(inst->pipe)) {
LOG_FATAL(LOGF_Nameserv, "pipe() failed"); LOG_FATAL("pipe() failed");
} }
UTI_FdSetCloexec(inst->pipe[0]); UTI_FdSetCloexec(inst->pipe[0]);
@@ -120,7 +120,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
assert(resolving_threads <= 1); assert(resolving_threads <= 1);
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) { if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed"); LOG_FATAL("pthread_create() failed");
} }
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst); SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst);

3
ntp.h
View File

@@ -38,6 +38,9 @@ typedef struct {
typedef uint32_t NTP_int32; typedef uint32_t NTP_int32;
/* The UDP port number used by NTP */
#define NTP_PORT 123
/* The NTP protocol version that we support */ /* The NTP protocol version that we support */
#define NTP_VERSION 4 #define NTP_VERSION 4

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -49,6 +49,7 @@
/* ================================================== */ /* ================================================== */
static LOG_FileID logfileid; static LOG_FileID logfileid;
static int log_raw_measurements;
/* ================================================== */ /* ================================================== */
/* Enumeration used for remembering the operating mode of one of the /* Enumeration used for remembering the operating mode of one of the
@@ -138,9 +139,8 @@ struct NCR_Instance_Record {
uint32_t auth_key_id; /* The ID of the authentication key to uint32_t auth_key_id; /* The ID of the authentication key to
use. */ use. */
/* Count of how many packets we have transmitted since last successful /* Count of transmitted packets since last valid response */
receive from this peer */ unsigned int tx_count;
int tx_count;
/* Flag indicating a valid response was received since last request */ /* Flag indicating a valid response was received since last request */
int valid_rx; int valid_rx;
@@ -178,6 +178,9 @@ struct NCR_Instance_Record {
NTP_int64 local_ntp_tx; NTP_int64 local_ntp_tx;
NTP_Local_Timestamp local_tx; NTP_Local_Timestamp local_tx;
/* Previous local transmit timestamp for the interleaved mode */
NTP_Local_Timestamp prev_local_tx;
/* The instance record in the main source management module. This /* The instance record in the main source management module. This
performs the statistical analysis on the samples we generate */ performs the statistical analysis on the samples we generate */
@@ -205,7 +208,8 @@ static ARR_Instance broadcasts;
/* Spacing required between samples for any two servers/peers (to /* Spacing required between samples for any two servers/peers (to
minimise risk of network collisions) (in seconds) */ minimise risk of network collisions) (in seconds) */
#define SAMPLING_SEPARATION 0.2 #define MIN_SAMPLING_SEPARATION 0.02
#define MAX_SAMPLING_SEPARATION 0.2
/* Randomness added to spacing between samples for one server/peer */ /* Randomness added to spacing between samples for one server/peer */
#define SAMPLING_RANDOMNESS 0.02 #define SAMPLING_RANDOMNESS 0.02
@@ -243,8 +247,14 @@ static ARR_Instance broadcasts;
/* Maximum acceptable delay in transmission for timestamp correction */ /* Maximum acceptable delay in transmission for timestamp correction */
#define MAX_TX_DELAY 1.0 #define MAX_TX_DELAY 1.0
/* Maximum allowed values of maxdelay parameters */
#define MAX_MAXDELAY 1.0e3
#define MAX_MAXDELAYRATIO 1.0e6
#define MAX_MAXDELAYDEVRATIO 1.0e6
/* Minimum and maximum allowed poll interval */ /* Minimum and maximum allowed poll interval */
#define MIN_POLL 0 #define MIN_MINPOLL -4
#define MIN_MAXPOLL 0
#define MAX_POLL 24 #define MAX_POLL 24
/* Kiss-o'-Death codes */ /* Kiss-o'-Death codes */
@@ -253,6 +263,17 @@ static ARR_Instance broadcasts;
/* Maximum poll interval set by KoD RATE */ /* Maximum poll interval set by KoD RATE */
#define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL #define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL
/* Maximum number of missed responses to follow peer's polling interval */
#define MAX_PEER_POLL_TX 8
/* Maximum number of missed responses to accept samples using old timestamps
in the interleaved client/server mode */
#define MAX_CLIENT_INTERLEAVED_TX 4
/* Maximum ratio of local intervals in the timestamp selection of the
interleaved mode to prefer a sample using previous timestamps */
#define MAX_INTERLEAVED_L2L_RATIO 0.1
/* Invalid socket, different from the one in ntp_io.c */ /* Invalid socket, different from the one in ntp_io.c */
#define INVALID_SOCK_FD -2 #define INVALID_SOCK_FD -2
@@ -273,6 +294,7 @@ static const char tss_chars[3] = {'D', 'K', 'H'};
static void transmit_timeout(void *arg); static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx); static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
static double get_separation(int poll);
/* ================================================== */ /* ================================================== */
@@ -327,25 +349,33 @@ do_time_checks(void)
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
if (ts2.tv_sec - now.tv_sec < warning_advance) if (ts2.tv_sec - now.tv_sec < warning_advance)
LOG(LOGS_WARN, LOGF_NtpCore, "Assumed NTP time ends at %s!", LOG(LOGS_WARN, "Assumed NTP time ends at %s!", UTI_TimeToLogForm(ts2.tv_sec));
UTI_TimeToLogForm(ts2.tv_sec));
#else #else
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
if (now.tv_sec > 0x7fffffff - warning_advance) if (now.tv_sec > 0x7fffffff - warning_advance)
LOG(LOGS_WARN, LOGF_NtpCore, "System time ends at %s!", LOG(LOGS_WARN, "System time ends at %s!", UTI_TimeToLogForm(0x7fffffff));
UTI_TimeToLogForm(0x7fffffff));
#endif #endif
} }
/* ================================================== */ /* ================================================== */
static void
zero_local_timestamp(NTP_Local_Timestamp *ts)
{
UTI_ZeroTimespec(&ts->ts);
ts->err = 0.0;
ts->source = NTP_TS_DAEMON;
}
/* ================================================== */
void void
NCR_Initialise(void) NCR_Initialise(void)
{ {
do_size_checks(); do_size_checks();
do_time_checks(); do_time_checks();
logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements", logfileid = CNF_GetLogMeasurements(&log_raw_measurements) ? LOG_FileOpen("measurements",
" Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp. Refid MTxRx") " Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp. Refid MTxRx")
: -1; : -1;
@@ -393,7 +423,7 @@ restart_timeout(NCR_Instance inst, double delay)
SCH_RemoveTimeout(inst->tx_timeout_id); SCH_RemoveTimeout(inst->tx_timeout_id);
/* Start new timer for transmission */ /* Start new timer for transmission */
inst->tx_timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION, inst->tx_timeout_id = SCH_AddTimeoutInClass(delay, get_separation(inst->local_poll),
SAMPLING_RANDOMNESS, SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass, SCH_NtpSamplingClass,
transmit_timeout, (void *)inst); transmit_timeout, (void *)inst);
@@ -474,6 +504,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->remote_addr = *remote_addr; result->remote_addr = *remote_addr;
result->local_addr.ip_addr.family = IPADDR_UNSPEC; result->local_addr.ip_addr.family = IPADDR_UNSPEC;
result->local_addr.if_index = INVALID_IF_INDEX;
switch (type) { switch (type) {
case NTP_SERVER: case NTP_SERVER:
@@ -492,12 +523,12 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->interleaved = params->interleaved; result->interleaved = params->interleaved;
result->minpoll = params->minpoll; result->minpoll = params->minpoll;
if (result->minpoll < MIN_POLL) if (result->minpoll < MIN_MINPOLL)
result->minpoll = SRC_DEFAULT_MINPOLL; result->minpoll = SRC_DEFAULT_MINPOLL;
else if (result->minpoll > MAX_POLL) else if (result->minpoll > MAX_POLL)
result->minpoll = MAX_POLL; result->minpoll = MAX_POLL;
result->maxpoll = params->maxpoll; result->maxpoll = params->maxpoll;
if (result->maxpoll < MIN_POLL) if (result->maxpoll < MIN_MAXPOLL)
result->maxpoll = SRC_DEFAULT_MAXPOLL; result->maxpoll = SRC_DEFAULT_MAXPOLL;
else if (result->maxpoll > MAX_POLL) else if (result->maxpoll > MAX_POLL)
result->maxpoll = MAX_POLL; result->maxpoll = MAX_POLL;
@@ -513,9 +544,9 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
if (result->presend_minpoll <= MAX_POLL && result->mode != MODE_CLIENT) if (result->presend_minpoll <= MAX_POLL && result->mode != MODE_CLIENT)
result->presend_minpoll = MAX_POLL + 1; result->presend_minpoll = MAX_POLL + 1;
result->max_delay = params->max_delay; result->max_delay = CLAMP(0.0, params->max_delay, MAX_MAXDELAY);
result->max_delay_ratio = params->max_delay_ratio; result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO);
result->max_delay_dev_ratio = params->max_delay_dev_ratio; result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
result->offset_correction = params->offset; result->offset_correction = params->offset;
result->auto_offline = params->auto_offline; result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target; result->poll_target = params->poll_target;
@@ -529,11 +560,11 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->auth_mode = AUTH_SYMMETRIC; result->auth_mode = AUTH_SYMMETRIC;
result->auth_key_id = params->authkey; result->auth_key_id = params->authkey;
if (!KEY_KeyKnown(result->auth_key_id)) { if (!KEY_KeyKnown(result->auth_key_id)) {
LOG(LOGS_WARN, LOGF_NtpCore, "Key %"PRIu32" used by source %s is %s", LOG(LOGS_WARN, "Key %"PRIu32" used by source %s is %s",
result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr), result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr),
"missing"); "missing");
} else if (!KEY_CheckKeyLength(result->auth_key_id)) { } else if (!KEY_CheckKeyLength(result->auth_key_id)) {
LOG(LOGS_WARN, LOGF_NtpCore, "Key %"PRIu32" used by source %s is %s", LOG(LOGS_WARN, "Key %"PRIu32" used by source %s is %s",
result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr), result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr),
"too short"); "too short");
} }
@@ -559,9 +590,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->opmode = params->online ? MD_ONLINE : MD_OFFLINE; result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
result->local_poll = result->minpoll; result->local_poll = result->minpoll;
result->poll_score = 0.0; result->poll_score = 0.0;
UTI_ZeroTimespec(&result->local_tx.ts); zero_local_timestamp(&result->local_tx);
result->local_tx.err = 0.0;
result->local_tx.source = NTP_TS_DAEMON;
result->burst_good_samples_to_go = 0; result->burst_good_samples_to_go = 0;
result->burst_total_samples_to_go = 0; result->burst_total_samples_to_go = 0;
memset(&result->report, 0, sizeof (result->report)); memset(&result->report, 0, sizeof (result->report));
@@ -624,9 +653,8 @@ NCR_ResetInstance(NCR_Instance instance)
UTI_ZeroNtp64(&instance->remote_ntp_tx); UTI_ZeroNtp64(&instance->remote_ntp_tx);
UTI_ZeroNtp64(&instance->local_ntp_rx); UTI_ZeroNtp64(&instance->local_ntp_rx);
UTI_ZeroNtp64(&instance->local_ntp_tx); UTI_ZeroNtp64(&instance->local_ntp_tx);
UTI_ZeroTimespec(&instance->local_rx.ts); zero_local_timestamp(&instance->local_rx);
instance->local_rx.err = 0.0; zero_local_timestamp(&instance->prev_local_tx);
instance->local_rx.source = NTP_TS_DAEMON;
} }
/* ================================================== */ /* ================================================== */
@@ -648,6 +676,7 @@ NCR_ResetPoll(NCR_Instance instance)
void void
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr) NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
{ {
memset(&inst->report, 0, sizeof (inst->report));
NCR_ResetInstance(inst); NCR_ResetInstance(inst);
inst->remote_addr = *remote_addr; inst->remote_addr = *remote_addr;
@@ -656,6 +685,7 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
else { else {
NIO_CloseServerSocket(inst->local_addr.sock_fd); NIO_CloseServerSocket(inst->local_addr.sock_fd);
inst->local_addr.ip_addr.family = IPADDR_UNSPEC; inst->local_addr.ip_addr.family = IPADDR_UNSPEC;
inst->local_addr.if_index = INVALID_IF_INDEX;
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr); inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
} }
@@ -698,19 +728,12 @@ static double
get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance) get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance)
{ {
double poll_adj; double poll_adj;
int samples;
if (error_in_estimate > peer_distance) { if (error_in_estimate > peer_distance) {
int shift = 0; poll_adj = -log(error_in_estimate / peer_distance) / log(2.0);
unsigned long temp = (int)(error_in_estimate / peer_distance);
do {
shift++;
temp>>=1;
} while (temp);
poll_adj = -shift - inst->poll_score + 0.5;
} else { } else {
int samples = SST_Samples(SRC_GetSourcestats(inst->source)); samples = SST_Samples(SRC_GetSourcestats(inst->source));
/* Adjust polling interval so that the number of sourcestats samples /* Adjust polling interval so that the number of sourcestats samples
remains close to the target value */ remains close to the target value */
@@ -727,6 +750,24 @@ get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance)
/* ================================================== */ /* ================================================== */
static int
get_transmit_poll(NCR_Instance inst)
{
int poll;
poll = inst->local_poll;
/* In symmetric mode, if the peer is responding, use shorter of the local
and remote poll interval, but not shorter than the minimum */
if (inst->mode == MODE_ACTIVE && poll > inst->remote_poll &&
inst->tx_count < MAX_PEER_POLL_TX)
poll = MAX(inst->remote_poll, inst->minpoll);
return poll;
}
/* ================================================== */
static double static double
get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx) get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
{ {
@@ -745,45 +786,26 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
we're in client/server mode, we don't care what poll interval the we're in client/server mode, we don't care what poll interval the
server responded with last time. */ server responded with last time. */
poll_to_use = get_transmit_poll(inst);
delay_time = UTI_Log2ToDouble(poll_to_use);
switch (inst->opmode) { switch (inst->opmode) {
case MD_OFFLINE: case MD_OFFLINE:
assert(0); assert(0);
break; break;
case MD_ONLINE: case MD_ONLINE:
/* Normal processing, depending on whether we're in
client/server or symmetric mode */
switch(inst->mode) { switch(inst->mode) {
case MODE_CLIENT: case MODE_CLIENT:
/* Client/server association - aim at some randomised time
approx the poll interval away */
poll_to_use = inst->local_poll;
delay_time = (double) (1UL<<poll_to_use);
if (inst->presend_done) if (inst->presend_done)
delay_time = WARM_UP_DELAY; delay_time = WARM_UP_DELAY;
break; break;
case MODE_ACTIVE: case MODE_ACTIVE:
/* Symmetric active association - aim at some randomised time approx /* If the remote stratum is higher than ours, wait a bit for the next
the poll interval away since the last transmit */ packet before responding in order to minimize the delay of the
measurement and its error for the peer which has higher stratum.
/* Use shorter of the local and remote poll interval, but not shorter If the remote stratum is equal to ours, try to interleave packets
than the allowed minimum */ evenly with the peer. */
poll_to_use = inst->local_poll;
if (poll_to_use > inst->remote_poll)
poll_to_use = inst->remote_poll;
if (poll_to_use < inst->minpoll)
poll_to_use = inst->minpoll;
delay_time = (double) (1UL<<poll_to_use);
/* If the remote stratum is higher than ours, try to lock on the
peer's polling to minimize our response time by slightly extending
our delay or waiting for the peer to catch up with us as the
random part in the actual interval is reduced. If the remote
stratum is equal to ours, try to interleave evenly with the peer. */
stratum_diff = inst->remote_stratum - REF_GetOurStratum(); stratum_diff = inst->remote_stratum - REF_GetOurStratum();
if ((stratum_diff > 0 && last_tx * PEER_SAMPLING_ADJ < delay_time) || if ((stratum_diff > 0 && last_tx * PEER_SAMPLING_ADJ < delay_time) ||
(!on_tx && !stratum_diff && (!on_tx && !stratum_diff &&
@@ -816,6 +838,21 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
return delay_time; return delay_time;
} }
/* ================================================== */
/* Calculate sampling separation for given polling interval */
static double
get_separation(int poll)
{
double separation;
/* Allow up to 8 sources using the same short interval to not be limited
by the separation */
separation = UTI_Log2ToDouble(poll - 3);
return CLAMP(MIN_SAMPLING_SEPARATION, separation, MAX_SAMPLING_SEPARATION);
}
/* ================================================== */ /* ================================================== */
/* Timeout handler for closing the client socket when no acceptable /* Timeout handler for closing the client socket when no acceptable
reply can be received from the server */ reply can be received from the server */
@@ -825,7 +862,7 @@ receive_timeout(void *arg)
{ {
NCR_Instance inst = (NCR_Instance)arg; NCR_Instance inst = (NCR_Instance)arg;
DEBUG_LOG(LOGF_NtpCore, "Receive timeout for [%s:%d]", DEBUG_LOG("Receive timeout for [%s:%d]",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port); UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
inst->rx_timeout_id = 0; inst->rx_timeout_id = 0;
@@ -951,7 +988,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
frequency all along */ frequency all along */
UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz); UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz);
/* Prepare random bits which will be added to the transmit timestamp. */ do {
/* Prepare random bits which will be added to the transmit timestamp */
UTI_GetNtp64Fuzz(&ts_fuzz, precision); UTI_GetNtp64Fuzz(&ts_fuzz, precision);
/* Transmit - this our local time right now! Also, we might need to /* Transmit - this our local time right now! Also, we might need to
@@ -964,11 +1002,11 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
length = NTP_NORMAL_PACKET_LENGTH; length = NTP_NORMAL_PACKET_LENGTH;
/* Authenticate the packet if needed */ /* Authenticate the packet */
if (auth_mode == AUTH_SYMMETRIC || auth_mode == AUTH_MSSNTP) { if (auth_mode == AUTH_SYMMETRIC || auth_mode == AUTH_MSSNTP) {
/* Pre-compensate the transmit time by approx. how long it will /* Pre-compensate the transmit time by approximately how long it will
take to generate the authentication data. */ take to generate the authentication data */
local_transmit.tv_nsec += auth_mode == AUTH_SYMMETRIC ? local_transmit.tv_nsec += auth_mode == AUTH_SYMMETRIC ?
KEY_GetAuthDelay(key_id) : NSD_GetAuthDelay(key_id); KEY_GetAuthDelay(key_id) : NSD_GetAuthDelay(key_id);
UTI_NormaliseTimespec(&local_transmit); UTI_NormaliseTimespec(&local_transmit);
@@ -981,7 +1019,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
(unsigned char *)&message.auth_data, (unsigned char *)&message.auth_data,
sizeof (message.auth_data)); sizeof (message.auth_data));
if (!auth_len) { if (!auth_len) {
DEBUG_LOG(LOGF_NtpCore, "Could not generate auth data with key %"PRIu32, key_id); DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0; return 0;
} }
@@ -1003,6 +1041,11 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
&message.transmit_ts, &ts_fuzz); &message.transmit_ts, &ts_fuzz);
} }
/* Avoid sending messages with non-zero transmit timestamp equal to the
receive timestamp to allow reliable detection of the interleaved mode */
} while (!UTI_CompareNtp64(&message.transmit_ts, &message.receive_ts) &&
!UTI_IsZeroNtp64(&message.transmit_ts));
ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL); ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
if (local_tx) { if (local_tx) {
@@ -1052,7 +1095,7 @@ transmit_timeout(void *arg)
return; return;
} }
DEBUG_LOG(LOGF_NtpCore, "Transmit timeout for [%s:%d]", DEBUG_LOG("Transmit timeout for [%s:%d]",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port); UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
/* Open new client socket */ /* Open new client socket */
@@ -1064,6 +1107,7 @@ transmit_timeout(void *arg)
/* Don't require the packet to be sent from the same address as before */ /* Don't require the packet to be sent from the same address as before */
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = inst->local_addr.sock_fd; local_addr.sock_fd = inst->local_addr.sock_fd;
/* Check whether we need to 'warm up' the link to the other end by /* Check whether we need to 'warm up' the link to the other end by
@@ -1141,12 +1185,12 @@ check_packet_format(NTP_Packet *message, int length)
version = NTP_LVM_TO_VERSION(message->lvm); version = NTP_LVM_TO_VERSION(message->lvm);
if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) { if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) {
DEBUG_LOG(LOGF_NtpCore, "NTP packet has invalid version %d", version); DEBUG_LOG("NTP packet has invalid version %d", version);
return 0; return 0;
} }
if (length < NTP_NORMAL_PACKET_LENGTH || (unsigned int)length % 4) { if (length < NTP_NORMAL_PACKET_LENGTH || (unsigned int)length % 4) {
DEBUG_LOG(LOGF_NtpCore, "NTP packet has invalid length %d", length); DEBUG_LOG("NTP packet has invalid length %d", length);
return 0; return 0;
} }
@@ -1307,9 +1351,9 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
measurement in seconds */ measurement in seconds */
double error_in_estimate; double error_in_estimate;
double remote_interval, local_interval, server_interval; NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time;
double delay_time, precision; double delay_time, precision;
NTP_Timestamp_Source sample_rx_tss;
/* ==================== */ /* ==================== */
@@ -1379,45 +1423,48 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
if (synced_packet && (!interleaved_packet || inst->valid_timestamps)) { if (synced_packet && (!interleaved_packet || inst->valid_timestamps)) {
/* These are the timespec equivalents of the remote and local epochs */ /* These are the timespec equivalents of the remote and local epochs */
struct timespec remote_receive, remote_transmit, prev_remote_receive; struct timespec remote_receive, remote_transmit, remote_request_receive;
struct timespec local_average, remote_average; struct timespec local_average, remote_average;
double rx_ts_err;
precision = LCL_GetSysPrecisionAsQuantum() +
UTI_Log2ToDouble(message->precision);
SST_GetFrequencyRange(stats, &source_freq_lo, &source_freq_hi);
/* Select remote and local timestamps for the new sample */
if (interleaved_packet) {
/* Prefer previous local TX and remote RX timestamps if it will make
the intervals significantly shorter in order to improve the accuracy
of the measured delay */
if (!UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
MAX_INTERLEAVED_L2L_RATIO *
UTI_DiffTimespecsToDouble(&inst->local_tx.ts, &inst->local_rx.ts) >
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->prev_local_tx.ts)) {
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_receive);
remote_request_receive = remote_receive;
local_transmit = inst->prev_local_tx;
} else {
UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive);
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_request_receive);
local_transmit = inst->local_tx;
}
UTI_Ntp64ToTimespec(&message->transmit_ts, &remote_transmit);
local_receive = inst->local_rx;
} else {
UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive); UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive);
UTI_Ntp64ToTimespec(&message->transmit_ts, &remote_transmit); UTI_Ntp64ToTimespec(&message->transmit_ts, &remote_transmit);
remote_request_receive = remote_receive;
/* Calculate intervals between remote and local timestamps */ local_receive = *rx_ts;
if (interleaved_packet) { local_transmit = inst->local_tx;
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &prev_remote_receive);
UTI_AverageDiffTimespecs(&remote_transmit, &remote_receive,
&remote_average, &remote_interval);
UTI_AverageDiffTimespecs(&inst->local_rx.ts, &inst->local_tx.ts,
&local_average, &local_interval);
server_interval = UTI_DiffTimespecsToDouble(&remote_transmit,
&prev_remote_receive);
rx_ts_err = inst->local_rx.err;
sample_rx_tss = inst->local_rx.source;
} else {
UTI_AverageDiffTimespecs(&remote_receive, &remote_transmit,
&remote_average, &remote_interval);
UTI_AverageDiffTimespecs(&inst->local_tx.ts, &rx_ts->ts,
&local_average, &local_interval);
server_interval = remote_interval;
rx_ts_err = rx_ts->err;
sample_rx_tss = rx_ts->source;
} }
/* Work out 'delay' relative to the source's time */ /* Calculate intervals between remote and local timestamps */
delay = (1.0 - (source_freq_lo + source_freq_hi) / 2.0) * UTI_AverageDiffTimespecs(&remote_receive, &remote_transmit,
local_interval - remote_interval; &remote_average, &remote_interval);
UTI_AverageDiffTimespecs(&local_transmit.ts, &local_receive.ts,
&local_average, &local_interval);
response_time = fabs(UTI_DiffTimespecsToDouble(&remote_transmit,
&remote_request_receive));
/* Clamp delay to avoid misleading results later */ precision = LCL_GetSysPrecisionAsQuantum() + UTI_Log2ToDouble(message->precision);
delay = fabs(delay);
/* Calculate delay */
delay = fabs(local_interval - remote_interval);
if (delay < precision) if (delay < precision)
delay = precision; delay = precision;
@@ -1435,21 +1482,27 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
sample pair. */ sample pair. */
sample_time = local_average; sample_time = local_average;
SST_GetFrequencyRange(stats, &source_freq_lo, &source_freq_hi);
/* Calculate skew */ /* Calculate skew */
skew = (source_freq_hi - source_freq_lo) / 2.0; skew = (source_freq_hi - source_freq_lo) / 2.0;
/* and then calculate peer dispersion */ /* and then calculate peer dispersion */
dispersion = precision + inst->local_tx.err + rx_ts_err + skew * fabs(local_interval); dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
skew * fabs(local_interval);
/* Additional tests required to pass before accumulating the sample */ /* Additional tests required to pass before accumulating the sample */
/* Test A requires that the minimum estimate of the peer delay is not /* Test A requires that the minimum estimate of the peer delay is not
larger than the configured maximum, in client mode that the server larger than the configured maximum, in client mode that the server
processing time is sane, and in the interleaved symmetric mode that processing time is sane, in the interleaved client mode that the
the delay is not longer than half of the remote polling interval to timestamps are not too old, and in the interleaved symmetric mode
detect missed packets */ that the delay is not longer than half of the remote polling interval
testA = delay - dispersion <= inst->max_delay && to detect missed packets */
!(inst->mode == MODE_CLIENT && server_interval > MAX_SERVER_INTERVAL) && testA = delay - dispersion <= inst->max_delay && precision <= inst->max_delay &&
!(inst->mode == MODE_CLIENT &&
(response_time > MAX_SERVER_INTERVAL ||
(interleaved_packet && inst->tx_count > MAX_CLIENT_INTERLEAVED_TX + 1))) &&
!(inst->mode == MODE_ACTIVE && interleaved_packet && !(inst->mode == MODE_ACTIVE && interleaved_packet &&
delay > UTI_Log2ToDouble(message->poll - 1)); delay > UTI_Log2ToDouble(message->poll - 1));
@@ -1473,10 +1526,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
testD = message->stratum <= 1 || REF_GetMode() != REF_ModeNormal || testD = message->stratum <= 1 || REF_GetMode() != REF_ModeNormal ||
pkt_refid != UTI_IPToRefid(&local_addr->ip_addr); pkt_refid != UTI_IPToRefid(&local_addr->ip_addr);
} else { } else {
remote_interval = local_interval = server_interval = 0.0; remote_interval = local_interval = response_time = 0.0;
offset = delay = dispersion = 0.0; offset = delay = dispersion = 0.0;
sample_time = rx_ts->ts; sample_time = rx_ts->ts;
sample_rx_tss = rx_ts->source; local_receive = *rx_ts;
local_transmit = inst->local_tx;
testA = testB = testC = testD = 0; testA = testB = testC = testD = 0;
} }
@@ -1505,6 +1559,12 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->local_rx = *rx_ts; inst->local_rx = *rx_ts;
inst->valid_timestamps = synced_packet; inst->valid_timestamps = synced_packet;
inst->updated_timestamps = 1; inst->updated_timestamps = 1;
/* Don't use the same set of timestamps for the next sample */
if (interleaved_packet)
inst->prev_local_tx = inst->local_tx;
else
zero_local_timestamp(&inst->prev_local_tx);
} }
/* Accept at most one response per request. The NTP specification recommends /* Accept at most one response per request. The NTP specification recommends
@@ -1522,36 +1582,36 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->valid_rx = 1; inst->valid_rx = 1;
} }
if ((int)sample_rx_tss < 0 || sample_rx_tss >= sizeof (tss_chars) || if ((unsigned int)local_receive.source >= sizeof (tss_chars) ||
(int)inst->local_tx.source < 0 || inst->local_tx.source >= sizeof (tss_chars)) (unsigned int)local_transmit.source >= sizeof (tss_chars))
assert(0); assert(0);
DEBUG_LOG(LOGF_NtpCore, "NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%f root_disp=%f refid=%"PRIx32" [%s]", DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%f root_disp=%f refid=%"PRIx32" [%s]",
message->lvm, message->stratum, message->poll, message->precision, message->lvm, message->stratum, message->poll, message->precision,
pkt_root_delay, pkt_root_dispersion, pkt_refid, pkt_root_delay, pkt_root_dispersion, pkt_refid,
message->stratum == NTP_INVALID_STRATUM ? UTI_RefidToString(pkt_refid) : ""); message->stratum == NTP_INVALID_STRATUM ? UTI_RefidToString(pkt_refid) : "");
DEBUG_LOG(LOGF_NtpCore, "reference=%s origin=%s receive=%s transmit=%s", DEBUG_LOG("reference=%s origin=%s receive=%s transmit=%s",
UTI_Ntp64ToString(&message->reference_ts), UTI_Ntp64ToString(&message->reference_ts),
UTI_Ntp64ToString(&message->originate_ts), UTI_Ntp64ToString(&message->originate_ts),
UTI_Ntp64ToString(&message->receive_ts), UTI_Ntp64ToString(&message->receive_ts),
UTI_Ntp64ToString(&message->transmit_ts)); UTI_Ntp64ToString(&message->transmit_ts));
DEBUG_LOG(LOGF_NtpCore, "offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f", DEBUG_LOG("offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f",
offset, delay, dispersion, root_delay, root_dispersion); offset, delay, dispersion, root_delay, root_dispersion);
DEBUG_LOG(LOGF_NtpCore, "remote_interval=%.9f local_interval=%.9f server_interval=%.9f txs=%c rxs=%c", DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
remote_interval, local_interval, server_interval, remote_interval, local_interval, response_time,
tss_chars[inst->local_tx.source], tss_chars[sample_rx_tss]); tss_chars[local_transmit.source], tss_chars[local_receive.source]);
DEBUG_LOG(LOGF_NtpCore, "test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d presend=%d valid=%d good=%d updated=%d", DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d presend=%d valid=%d good=%d updated=%d",
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD, test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
kod_rate, interleaved_packet, inst->presend_done, valid_packet, good_packet, kod_rate, interleaved_packet, inst->presend_done, valid_packet, good_packet,
!UTI_CompareTimespecs(&inst->local_rx.ts, &rx_ts->ts)); !UTI_CompareTimespecs(&inst->local_rx.ts, &rx_ts->ts));
if (valid_packet) { if (valid_packet) {
if (synced_packet) {
inst->remote_poll = message->poll; inst->remote_poll = message->poll;
inst->remote_stratum = message->stratum; inst->remote_stratum = message->stratum != NTP_INVALID_STRATUM ?
message->stratum : NTP_MAX_STRATUM;
inst->tx_count = 0; inst->tx_count = 0;
SRC_UpdateReachability(inst->source, 1); SRC_UpdateReachability(inst->source, synced_packet);
}
if (good_packet) { if (good_packet) {
/* Do this before we accumulate a new sample into the stats registers, obviously */ /* Do this before we accumulate a new sample into the stats registers, obviously */
@@ -1591,7 +1651,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
default: default:
break; break;
} }
} else if (synced_packet) { } else {
/* Slowly increase the polling interval if we can't get good packet */ /* Slowly increase the polling interval if we can't get good packet */
adjust_poll(inst, 0.1); adjust_poll(inst, 0.1);
} }
@@ -1600,8 +1660,9 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
server and the socket can be closed */ server and the socket can be closed */
close_client_socket(inst); close_client_socket(inst);
/* Update the local address */ /* Update the local address and interface */
inst->local_addr.ip_addr = local_addr->ip_addr; inst->local_addr.ip_addr = local_addr->ip_addr;
inst->local_addr.if_index = local_addr->if_index;
/* And now, requeue the timer */ /* And now, requeue the timer */
if (inst->opmode != MD_OFFLINE) { if (inst->opmode != MD_OFFLINE) {
@@ -1609,13 +1670,14 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts)); UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts));
if (kod_rate) { if (kod_rate) {
LOG(LOGS_WARN, "Received KoD RATE from %s",
UTI_IPToString(&inst->remote_addr.ip_addr));
/* Back off for a while and stop ongoing burst */ /* Back off for a while and stop ongoing burst */
delay_time += 4 * (1UL << inst->minpoll); delay_time += 4 * UTI_Log2ToDouble(inst->local_poll);
if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) { if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) {
inst->burst_good_samples_to_go = 0; inst->burst_good_samples_to_go = 0;
LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s, burst sampling stopped",
UTI_IPToString(&inst->remote_addr.ip_addr));
} }
} }
@@ -1641,21 +1703,21 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->report.offset = offset; inst->report.offset = offset;
inst->report.peer_delay = delay; inst->report.peer_delay = delay;
inst->report.peer_dispersion = dispersion; inst->report.peer_dispersion = dispersion;
inst->report.response_time = server_interval; inst->report.response_time = response_time;
inst->report.jitter_asymmetry = SST_GetJitterAsymmetry(stats); inst->report.jitter_asymmetry = SST_GetJitterAsymmetry(stats);
inst->report.tests = ((((((((test1 << 1 | test2) << 1 | test3) << 1 | inst->report.tests = ((((((((test1 << 1 | test2) << 1 | test3) << 1 |
test5) << 1 | test6) << 1 | test7) << 1 | test5) << 1 | test6) << 1 | test7) << 1 |
testA) << 1 | testB) << 1 | testC) << 1 | testD; testA) << 1 | testB) << 1 | testC) << 1 | testD;
inst->report.interleaved = interleaved_packet; inst->report.interleaved = interleaved_packet;
inst->report.authenticated = inst->auth_mode != AUTH_NONE; inst->report.authenticated = inst->auth_mode != AUTH_NONE;
inst->report.tx_tss_char = tss_chars[inst->local_tx.source]; inst->report.tx_tss_char = tss_chars[local_transmit.source];
inst->report.rx_tss_char = tss_chars[sample_rx_tss]; inst->report.rx_tss_char = tss_chars[local_receive.source];
inst->report.total_valid_count++; inst->report.total_valid_count++;
} }
/* Do measurement logging */ /* Do measurement logging */
if (logfileid != -1) { if (logfileid != -1 && (log_raw_measurements || synced_packet)) {
LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d %1d%1d%1d %1d%1d%1d%d %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e %08"PRIX32" %1d%1c %1c %1c", LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d %1d%1d%1d %1d%1d%1d%d %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e %08"PRIX32" %1d%1c %1c %1c",
UTI_TimeToLogForm(sample_time.tv_sec), UTI_TimeToLogForm(sample_time.tv_sec),
UTI_IPToString(&inst->remote_addr.ip_addr), UTI_IPToString(&inst->remote_addr.ip_addr),
@@ -1667,8 +1729,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
offset, delay, dispersion, offset, delay, dispersion,
pkt_root_delay, pkt_root_dispersion, pkt_refid, pkt_root_delay, pkt_root_dispersion, pkt_refid,
NTP_LVM_TO_MODE(message->lvm), interleaved_packet ? 'I' : 'B', NTP_LVM_TO_MODE(message->lvm), interleaved_packet ? 'I' : 'B',
tss_chars[inst->local_tx.source], tss_chars[local_transmit.source],
tss_chars[sample_rx_tss]); tss_chars[local_receive.source]);
} }
return good_packet; return good_packet;
@@ -1796,15 +1858,14 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
if (proc_packet) { if (proc_packet) {
/* Check if the reply was received by the socket that sent the request */ /* Check if the reply was received by the socket that sent the request */
if (local_addr->sock_fd != inst->local_addr.sock_fd) { if (local_addr->sock_fd != inst->local_addr.sock_fd) {
DEBUG_LOG(LOGF_NtpCore, DEBUG_LOG("Packet received by wrong socket %d (expected %d)",
"Packet received by wrong socket %d (expected %d)",
local_addr->sock_fd, inst->local_addr.sock_fd); local_addr->sock_fd, inst->local_addr.sock_fd);
return 0; return 0;
} }
/* Ignore packets from offline sources */ /* Ignore packets from offline sources */
if (inst->opmode == MD_OFFLINE || inst->tx_suspended) { if (inst->opmode == MD_OFFLINE || inst->tx_suspended) {
DEBUG_LOG(LOGF_NtpCore, "Packet from offline source"); DEBUG_LOG("Packet from offline source");
return 0; return 0;
} }
@@ -1814,8 +1875,7 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
/* It's not a reply to our request, don't return success */ /* It's not a reply to our request, don't return success */
return 0; return 0;
} else { } else {
DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded pkt_mode=%d our_mode=%d", DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%d", pkt_mode, inst->mode);
pkt_mode, inst->mode);
return 0; return 0;
} }
} }
@@ -1831,14 +1891,13 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
NTP_Mode pkt_mode, my_mode; NTP_Mode pkt_mode, my_mode;
NTP_int64 *local_ntp_rx, *local_ntp_tx; NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx, *tx_ts; NTP_Local_Timestamp local_tx, *tx_ts;
int valid_auth, log_index, interleaved; int valid_auth, log_index, interleaved, poll;
AuthenticationMode auth_mode; AuthenticationMode auth_mode;
uint32_t key_id; uint32_t key_id;
/* Ignore the packet if it wasn't received by server socket */ /* Ignore the packet if it wasn't received by server socket */
if (!NIO_IsServerSocket(local_addr->sock_fd)) { if (!NIO_IsServerSocket(local_addr->sock_fd)) {
DEBUG_LOG(LOGF_NtpCore, "NTP request packet received by client socket %d", DEBUG_LOG("NTP request packet received by client socket %d", local_addr->sock_fd);
local_addr->sock_fd);
return; return;
} }
@@ -1846,7 +1905,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return; return;
if (!ADF_IsAllowed(access_auth_table, &remote_addr->ip_addr)) { if (!ADF_IsAllowed(access_auth_table, &remote_addr->ip_addr)) {
DEBUG_LOG(LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d", DEBUG_LOG("NTP packet received from unauthorised host %s port %d",
UTI_IPToString(&remote_addr->ip_addr), UTI_IPToString(&remote_addr->ip_addr),
remote_addr->port); remote_addr->port);
return; return;
@@ -1865,7 +1924,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
break; break;
default: default:
/* Discard */ /* Discard */
DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded pkt_mode=%d", pkt_mode); DEBUG_LOG("NTP packet discarded pkt_mode=%d", pkt_mode);
return; return;
} }
@@ -1873,7 +1932,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Don't reply to all requests if the rate is excessive */ /* Don't reply to all requests if the rate is excessive */
if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) { if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) {
DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded to limit response rate"); DEBUG_LOG("NTP packet discarded to limit response rate");
return; return;
} }
@@ -1891,7 +1950,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
break; break;
default: default:
/* Discard packets in other modes */ /* Discard packets in other modes */
DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded auth_mode=%d", auth_mode); DEBUG_LOG("NTP packet discarded auth_mode=%d", auth_mode);
return; return;
} }
} }
@@ -1911,10 +1970,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
!UTI_CompareNtp64(&message->originate_ts, local_ntp_rx); !UTI_CompareNtp64(&message->originate_ts, local_ntp_rx);
if (interleaved) { if (interleaved) {
if (!UTI_IsZeroNtp64(local_ntp_tx))
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts); UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
else
interleaved = 0;
tx_ts = &local_tx; tx_ts = &local_tx;
} else { } else {
UTI_ZeroNtp64(local_ntp_tx); UTI_ZeroNtp64(local_ntp_tx);
@@ -1922,8 +1978,13 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
} }
} }
/* Suggest the client to increase its polling interval if it indicates
the interval is shorter than the rate limiting interval */
poll = CLG_GetNtpMinPoll();
poll = MAX(poll, message->poll);
/* Send a reply */ /* Send a reply */
transmit_packet(my_mode, interleaved, message->poll, NTP_LVM_TO_VERSION(message->lvm), transmit_packet(my_mode, interleaved, poll, NTP_LVM_TO_VERSION(message->lvm),
auth_mode, key_id, &message->receive_ts, &message->transmit_ts, auth_mode, key_id, &message->receive_ts, &message->transmit_ts,
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr); rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr);
@@ -1941,27 +2002,27 @@ update_tx_timestamp(NTP_Local_Timestamp *tx_ts, NTP_Local_Timestamp *new_tx_ts,
double delay; double delay;
if (UTI_IsZeroTimespec(&tx_ts->ts)) { if (UTI_IsZeroTimespec(&tx_ts->ts)) {
DEBUG_LOG(LOGF_NtpCore, "Unexpected TX update"); DEBUG_LOG("Unexpected TX update");
return; return;
} }
/* Check if this is the last packet that was sent */ /* Check if this is the last packet that was sent */
if ((local_ntp_rx && UTI_CompareNtp64(&message->receive_ts, local_ntp_rx)) || if ((local_ntp_rx && UTI_CompareNtp64(&message->receive_ts, local_ntp_rx)) ||
(local_ntp_tx && UTI_CompareNtp64(&message->transmit_ts, local_ntp_tx))) { (local_ntp_tx && UTI_CompareNtp64(&message->transmit_ts, local_ntp_tx))) {
DEBUG_LOG(LOGF_NtpCore, "RX/TX timestamp mismatch"); DEBUG_LOG("RX/TX timestamp mismatch");
return; return;
} }
delay = UTI_DiffTimespecsToDouble(&new_tx_ts->ts, &tx_ts->ts); delay = UTI_DiffTimespecsToDouble(&new_tx_ts->ts, &tx_ts->ts);
if (delay < 0.0 || delay > MAX_TX_DELAY) { if (delay < 0.0 || delay > MAX_TX_DELAY) {
DEBUG_LOG(LOGF_NtpCore, "Unacceptable TX delay %.9f", delay); DEBUG_LOG("Unacceptable TX delay %.9f", delay);
return; return;
} }
*tx_ts = *new_tx_ts; *tx_ts = *new_tx_ts;
DEBUG_LOG(LOGF_NtpCore, "Updated TX timestamp delay=%.9f", delay); DEBUG_LOG("Updated TX timestamp delay=%.9f", delay);
} }
/* ================================================== */ /* ================================================== */
@@ -2007,10 +2068,10 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
if (log_index < 0) if (log_index < 0)
return; return;
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx); if (SMT_IsEnabled() && NTP_LVM_TO_MODE(message->lvm) == MODE_SERVER)
UTI_AddDoubleToTimespec(&tx_ts->ts, SMT_GetOffset(&tx_ts->ts), &tx_ts->ts);
if (UTI_IsZeroNtp64(local_ntp_tx)) CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
return;
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts); UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message); update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
@@ -2028,6 +2089,9 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
UTI_AdjustTimespec(&inst->local_rx.ts, when, &inst->local_rx.ts, &delta, dfreq, doffset); UTI_AdjustTimespec(&inst->local_rx.ts, when, &inst->local_rx.ts, &delta, dfreq, doffset);
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) if (!UTI_IsZeroTimespec(&inst->local_tx.ts))
UTI_AdjustTimespec(&inst->local_tx.ts, when, &inst->local_tx.ts, &delta, dfreq, doffset); UTI_AdjustTimespec(&inst->local_tx.ts, when, &inst->local_tx.ts, &delta, dfreq, doffset);
if (!UTI_IsZeroTimespec(&inst->prev_local_tx.ts))
UTI_AdjustTimespec(&inst->prev_local_tx.ts, when, &inst->prev_local_tx.ts, &delta, dfreq,
doffset);
} }
/* ================================================== */ /* ================================================== */
@@ -2040,7 +2104,7 @@ NCR_TakeSourceOnline(NCR_Instance inst)
/* Nothing to do */ /* Nothing to do */
break; break;
case MD_OFFLINE: case MD_OFFLINE:
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr)); LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
inst->opmode = MD_ONLINE; inst->opmode = MD_ONLINE;
NCR_ResetInstance(inst); NCR_ResetInstance(inst);
start_initial_timeout(inst); start_initial_timeout(inst);
@@ -2050,7 +2114,7 @@ NCR_TakeSourceOnline(NCR_Instance inst)
break; break;
case MD_BURST_WAS_OFFLINE: case MD_BURST_WAS_OFFLINE:
inst->opmode = MD_BURST_WAS_ONLINE; inst->opmode = MD_BURST_WAS_ONLINE;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr)); LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
break; break;
} }
} }
@@ -2062,14 +2126,14 @@ NCR_TakeSourceOffline(NCR_Instance inst)
{ {
switch (inst->opmode) { switch (inst->opmode) {
case MD_ONLINE: case MD_ONLINE:
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr)); LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
take_offline(inst); take_offline(inst);
break; break;
case MD_OFFLINE: case MD_OFFLINE:
break; break;
case MD_BURST_WAS_ONLINE: case MD_BURST_WAS_ONLINE:
inst->opmode = MD_BURST_WAS_OFFLINE; inst->opmode = MD_BURST_WAS_OFFLINE;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr)); LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
break; break;
case MD_BURST_WAS_OFFLINE: case MD_BURST_WAS_OFFLINE:
break; break;
@@ -2082,10 +2146,10 @@ NCR_TakeSourceOffline(NCR_Instance inst)
void void
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll) NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
{ {
if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL) if (new_minpoll < MIN_MINPOLL || new_minpoll > MAX_POLL)
return; return;
inst->minpoll = new_minpoll; inst->minpoll = new_minpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll); LOG(LOGS_INFO, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
if (inst->maxpoll < inst->minpoll) if (inst->maxpoll < inst->minpoll)
NCR_ModifyMaxpoll(inst, inst->minpoll); NCR_ModifyMaxpoll(inst, inst->minpoll);
} }
@@ -2095,10 +2159,10 @@ NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
void void
NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll) NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
{ {
if (new_maxpoll < MIN_POLL || new_maxpoll > MAX_POLL) if (new_maxpoll < MIN_MAXPOLL || new_maxpoll > MAX_POLL)
return; return;
inst->maxpoll = new_maxpoll; inst->maxpoll = new_maxpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll); LOG(LOGS_INFO, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);
if (inst->minpoll > inst->maxpoll) if (inst->minpoll > inst->maxpoll)
NCR_ModifyMinpoll(inst, inst->maxpoll); NCR_ModifyMinpoll(inst, inst->maxpoll);
} }
@@ -2108,9 +2172,9 @@ NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
void void
NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay) NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
{ {
inst->max_delay = new_max_delay; inst->max_delay = CLAMP(0.0, new_max_delay, MAX_MAXDELAY);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay %f", LOG(LOGS_INFO, "Source %s new max delay %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay); UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay);
} }
/* ================================================== */ /* ================================================== */
@@ -2118,9 +2182,9 @@ NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
void void
NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio) NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
{ {
inst->max_delay_ratio = new_max_delay_ratio; inst->max_delay_ratio = CLAMP(0.0, new_max_delay_ratio, MAX_MAXDELAYRATIO);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay ratio %f", LOG(LOGS_INFO, "Source %s new max delay ratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay_ratio); UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_ratio);
} }
/* ================================================== */ /* ================================================== */
@@ -2128,9 +2192,9 @@ NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
void void
NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio) NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio)
{ {
inst->max_delay_dev_ratio = new_max_delay_dev_ratio; inst->max_delay_dev_ratio = CLAMP(0.0, new_max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay dev ratio %f", LOG(LOGS_INFO, "Source %s new max delay dev ratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay_dev_ratio); UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_dev_ratio);
} }
/* ================================================== */ /* ================================================== */
@@ -2139,7 +2203,7 @@ void
NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum) NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum)
{ {
inst->min_stratum = new_min_stratum; inst->min_stratum = new_min_stratum;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minstratum %d", LOG(LOGS_INFO, "Source %s new minstratum %d",
UTI_IPToString(&inst->remote_addr.ip_addr), new_min_stratum); UTI_IPToString(&inst->remote_addr.ip_addr), new_min_stratum);
} }
@@ -2149,7 +2213,7 @@ void
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target) NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target)
{ {
inst->poll_target = new_poll_target; inst->poll_target = new_poll_target;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new polltarget %d", LOG(LOGS_INFO, "Source %s new polltarget %d",
UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target); UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target);
} }
@@ -2194,7 +2258,7 @@ NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_sampl
void void
NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now) NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now)
{ {
report->poll = inst->local_poll; report->poll = get_transmit_poll(inst);
switch (inst->mode) { switch (inst->mode) {
case MODE_CLIENT: case MODE_CLIENT:
@@ -2334,20 +2398,19 @@ broadcast_timeout(void *arg)
BroadcastDestination *destination; BroadcastDestination *destination;
NTP_int64 orig_ts; NTP_int64 orig_ts;
NTP_Local_Timestamp recv_ts; NTP_Local_Timestamp recv_ts;
int poll;
destination = ARR_GetElement(broadcasts, (long)arg); destination = ARR_GetElement(broadcasts, (long)arg);
poll = log(destination->interval) / log(2.0) + 0.5;
UTI_ZeroNtp64(&orig_ts); UTI_ZeroNtp64(&orig_ts);
UTI_ZeroTimespec(&recv_ts.ts); zero_local_timestamp(&recv_ts);
recv_ts.source = NTP_TS_DAEMON;
recv_ts.err = 0.0;
transmit_packet(MODE_BROADCAST, 0, log(destination->interval) / log(2.0) + 0.5, transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts,
NTP_VERSION, 0, 0, &orig_ts, &orig_ts, &recv_ts, NULL, NULL, NULL, NULL, NULL, NULL, &destination->addr, &destination->local_addr);
&destination->addr, &destination->local_addr);
/* Requeue timeout. We don't care if interval drifts gradually. */ /* Requeue timeout. We don't care if interval drifts gradually. */
SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_AddTimeoutInClass(destination->interval, get_separation(poll), SAMPLING_RANDOMNESS,
SCH_NtpBroadcastClass, broadcast_timeout, arg); SCH_NtpBroadcastClass, broadcast_timeout, arg);
} }
@@ -2363,10 +2426,11 @@ NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval)
destination->addr.ip_addr = *addr; destination->addr.ip_addr = *addr;
destination->addr.port = port; destination->addr.port = port;
destination->local_addr.ip_addr.family = IPADDR_UNSPEC; destination->local_addr.ip_addr.family = IPADDR_UNSPEC;
destination->local_addr.if_index = INVALID_IF_INDEX;
destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr); destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr);
destination->interval = CLAMP(1 << MIN_POLL, interval, 1 << MAX_POLL); destination->interval = CLAMP(1, interval, 1 << MAX_POLL);
SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_AddTimeoutInClass(destination->interval, MAX_SAMPLING_SEPARATION, SAMPLING_RANDOMNESS,
SCH_NtpBroadcastClass, broadcast_timeout, SCH_NtpBroadcastClass, broadcast_timeout,
(void *)(long)(ARR_GetSize(broadcasts) - 1)); (void *)(long)(ARR_GetSize(broadcasts) - 1));
} }

View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -130,10 +130,10 @@ prepare_socket(int family, int port_number, int client_only)
if (sock_fd < 0) { if (sock_fd < 0) {
if (!client_only) { 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)); UTI_SockaddrFamilyToString(family), strerror(errno));
} else { } 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)); UTI_SockaddrFamilyToString(family), strerror(errno));
} }
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
@@ -193,38 +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 */ /* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number && if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) { 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 */ /* Don't quit - we might survive anyway */
} }
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */ /* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (!client_only && if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) { 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 */ /* Don't quit - we might survive anyway */
} }
#ifdef SO_TIMESTAMP /* Enable kernel/HW timestamping of packets */
/* Enable receiving of timestamp control messages */ #ifdef HAVE_LINUX_TIMESTAMPING
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
#endif
#ifdef SO_TIMESTAMPNS #ifdef SO_TIMESTAMPNS
/* Try nanosecond resolution first */
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0) if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
#endif #endif
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) { #ifdef SO_TIMESTAMP
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP"); if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0)
/* Don't quit - we might survive anyway */ LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP");
}
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events);
#endif #endif
;
#ifdef IP_FREEBIND #ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */ /* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 && if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 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 #endif
@@ -232,7 +229,7 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef HAVE_IN_PKTINFO #ifdef HAVE_IN_PKTINFO
/* We want the local IP info on server sockets */ /* We want the local IP info on server sockets */
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { 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 */ /* Don't quit - we might survive anyway */
} }
#endif #endif
@@ -242,18 +239,18 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef IPV6_V6ONLY #ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */ /* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) { 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 #endif
#ifdef HAVE_IN6_PKTINFO #ifdef HAVE_IN6_PKTINFO
#ifdef IPV6_RECVPKTINFO #ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { 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 #else
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { 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
#endif #endif
@@ -262,7 +259,7 @@ prepare_socket(int family, int port_number, int client_only)
/* Bind the socket if a port or address was specified */ /* 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) { 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)); UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd); close(sock_fd);
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
@@ -304,7 +301,7 @@ connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
assert(addr_len); assert(addr_len);
if (connect(sock_fd, &addr.u, addr_len) < 0) { 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, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno)); strerror(errno));
return 0; return 0;
@@ -364,8 +361,11 @@ NIO_Initialise(int family)
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise(); NIO_Linux_Initialise();
#else #else
if (ARR_GetSize(CNF_GetHwTsInterfaces())) if (1) {
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported"); CNF_HwTsInterface *conf_iface;
if (CNF_GetHwTsInterface(0, &conf_iface))
LOG_FATAL("HW timestamping not supported");
}
#endif #endif
recv_messages = ARR_CreateInstance(sizeof (struct Message)); recv_messages = ARR_CreateInstance(sizeof (struct Message));
@@ -427,7 +427,7 @@ NIO_Initialise(int family)
&& client_sock_fd6 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD
#endif #endif
)) { )) {
LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets"); LOG_FATAL("Could not open NTP sockets");
} }
} }
@@ -575,14 +575,13 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
NTP_Local_Timestamp local_ts; NTP_Local_Timestamp local_ts;
struct timespec sched_ts; struct timespec sched_ts;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
int if_index;
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL); SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON; local_ts.source = NTP_TS_DAEMON;
sched_ts = local_ts.ts; sched_ts = local_ts.ts;
if (hdr->msg_namelen > sizeof (union sockaddr_in46)) { if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
DEBUG_LOG(LOGF_NtpIO, "Truncated source address"); DEBUG_LOG("Truncated source address");
return; return;
} }
@@ -595,17 +594,17 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
} }
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = sock_fd; local_addr.sock_fd = sock_fd;
if_index = -1;
if (hdr->msg_flags & MSG_TRUNC) { if (hdr->msg_flags & MSG_TRUNC) {
DEBUG_LOG(LOGF_NtpIO, "Received truncated message from %s:%d", DEBUG_LOG("Received truncated message from %s:%d",
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port); UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
return; return;
} }
if (hdr->msg_flags & MSG_CTRUNC) { if (hdr->msg_flags & MSG_CTRUNC) {
DEBUG_LOG(LOGF_NtpIO, "Truncated control message"); DEBUG_LOG("Truncated control message");
/* Continue */ /* Continue */
} }
@@ -617,7 +616,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr); local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4; local_addr.ip_addr.family = IPADDR_INET4;
if_index = ipi.ipi_ifindex; local_addr.if_index = ipi.ipi_ifindex;
} }
#endif #endif
@@ -629,7 +628,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr, memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6)); sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6; local_addr.ip_addr.family = IPADDR_INET6;
if_index = ipi.ipi6_ifindex; local_addr.if_index = ipi.ipi6_ifindex;
} }
#endif #endif
@@ -657,14 +656,13 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
} }
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length))
hdr, length, sock_fd, if_index))
return; return;
#endif #endif
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f", 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, length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, if_index, UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts)); local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
/* Just ignore the packet if it's not of a recognized length */ /* Just ignore the packet if it's not of a recognized length */
@@ -711,7 +709,7 @@ read_from_socket(int sock_fd, int event, void *anything)
#endif #endif
if (status < 0) { if (status < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not receive from fd %d : %s", sock_fd, DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
strerror(errno)); strerror(errno));
return; return;
} }
@@ -742,7 +740,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
assert(initialised); assert(initialised);
if (local_addr->sock_fd == INVALID_SOCK_FD) { 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); UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
return 0; return 0;
} }
@@ -818,14 +816,14 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
msg.msg_control = NULL; msg.msg_control = NULL;
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) { 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(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd, UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
strerror(errno)); strerror(errno));
return 0; return 0;
} }
DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", length, DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length,
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd); UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2016 * Copyright (C) Miroslav Lichvar 2016-2017
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -32,7 +32,6 @@
#include <linux/errqueue.h> #include <linux/errqueue.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
#include <linux/ptp_clock.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <net/if.h> #include <net/if.h>
@@ -61,17 +60,30 @@ struct Interface {
char name[IF_NAMESIZE]; char name[IF_NAMESIZE];
int if_index; int if_index;
int phc_fd; int phc_fd;
int phc_mode;
int phc_nocrossts;
/* Link speed in mbit/s */ /* Link speed in mbit/s */
int link_speed; int link_speed;
/* Start of UDP data at layer 2 for IPv4 and IPv6 */ /* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start; int l2_udp4_ntp_start;
int l2_udp6_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; HCL_Instance clock;
}; };
/* Number of PHC readings per HW clock sample */ /* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10 #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 */ /* Array of Interfaces */
static ARR_Instance interfaces; static ARR_Instance interfaces;
@@ -85,19 +97,18 @@ static int permanent_ts_options;
/* ================================================== */ /* ================================================== */
static int static int
add_interface(const char *name) add_interface(CNF_HwTsInterface *conf_iface)
{ {
struct ethtool_ts_info ts_info; struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config; struct hwtstamp_config ts_config;
struct ifreq req; struct ifreq req;
int sock_fd, if_index, phc_index, phc_fd; int sock_fd, if_index, phc_fd, req_hwts_flags;
unsigned int i; unsigned int i;
struct Interface *iface; struct Interface *iface;
char phc_path[64];
/* Check if the interface was not already added */ /* Check if the interface was not already added */
for (i = 0; i < ARR_GetSize(interfaces); i++) { for (i = 0; i < ARR_GetSize(interfaces); i++) {
if (!strcmp(name, ((struct Interface *)ARR_GetElement(interfaces, i))->name)) if (!strcmp(conf_iface->name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
return 1; return 1;
} }
@@ -108,13 +119,14 @@ add_interface(const char *name)
memset(&req, 0, sizeof (req)); memset(&req, 0, sizeof (req));
memset(&ts_info, 0, sizeof (ts_info)); memset(&ts_info, 0, sizeof (ts_info));
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", name) >= sizeof (req.ifr_name)) { if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
close(sock_fd); close(sock_fd);
return 0; return 0;
} }
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) { if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
close(sock_fd); close(sock_fd);
return 0; return 0;
} }
@@ -125,50 +137,73 @@ add_interface(const char *name)
req.ifr_data = (char *)&ts_info; req.ifr_data = (char *)&ts_info;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) { if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno)); 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); close(sock_fd);
return 0; return 0;
} }
ts_config.flags = 0; ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON; 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; ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
req.ifr_data = (char *)&ts_config; req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) { if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
close(sock_fd); close(sock_fd);
return 0; return 0;
} }
close(sock_fd); close(sock_fd);
phc_index = ts_info.phc_index;
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path)) phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
return 0; return 0;
phc_fd = open(phc_path, O_RDONLY);
if (phc_fd < 0) {
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not open %s : %s", phc_path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(phc_fd);
iface = ARR_GetNewElement(interfaces); iface = ARR_GetNewElement(interfaces);
snprintf(iface->name, sizeof (iface->name), "%s", name); snprintf(iface->name, sizeof (iface->name), "%s", conf_iface->name);
iface->if_index = if_index; iface->if_index = if_index;
iface->phc_fd = phc_fd; 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 */ /* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
iface->link_speed = 1000; iface->link_speed = 1000;
iface->l2_udp4_ntp_start = 42; iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62; iface->l2_udp6_ntp_start = 62;
iface->clock = HCL_CreateInstance(); iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", name); 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; return 1;
} }
@@ -176,18 +211,22 @@ add_interface(const char *name)
/* ================================================== */ /* ================================================== */
static int static int
add_all_interfaces(void) add_all_interfaces(CNF_HwTsInterface *conf_iface_all)
{ {
CNF_HwTsInterface conf_iface;
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
int r; int r;
conf_iface = *conf_iface_all;
if (getifaddrs(&ifaddr)) { if (getifaddrs(&ifaddr)) {
DEBUG_LOG(LOGF_NtpIOLinux, "getifaddrs() failed : %s", strerror(errno)); DEBUG_LOG("getifaddrs() failed : %s", strerror(errno));
return 0; return 0;
} }
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) { for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (add_interface(ifa->ifa_name)) conf_iface.name = ifa->ifa_name;
if (add_interface(&conf_iface))
r = 1; r = 1;
} }
@@ -218,7 +257,7 @@ update_interface_speed(struct Interface *iface)
req.ifr_data = (char *)&cmd; req.ifr_data = (char *)&cmd;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) { if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd); close(sock_fd);
return; return;
} }
@@ -230,45 +269,71 @@ update_interface_speed(struct Interface *iface)
/* ================================================== */ /* ================================================== */
#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 void
NIO_Linux_Initialise(void) NIO_Linux_Initialise(void)
{ {
ARR_Instance config_hwts_ifaces; CNF_HwTsInterface *conf_iface;
char *if_name;
unsigned int i; unsigned int i;
int wildcard, hwts; int hwts;
interfaces = ARR_CreateInstance(sizeof (struct Interface)); interfaces = ARR_CreateInstance(sizeof (struct Interface));
config_hwts_ifaces = CNF_GetHwTsInterfaces();
/* Enable HW timestamping on specified interfaces. If "*" was specified, try /* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */ all interfaces. If no interface was specified, enable SW timestamping. */
for (i = wildcard = 0; i < ARR_GetSize(config_hwts_ifaces); i++) { for (i = hwts = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (!strcmp("*", *(char **)ARR_GetElement(config_hwts_ifaces, i))) if (!strcmp("*", conf_iface->name))
wildcard = 1; continue;
if (!add_interface(conf_iface))
LOG_FATAL("Could not enable HW timestamping on %s", conf_iface->name);
hwts = 1;
} }
if (!wildcard && ARR_GetSize(config_hwts_ifaces)) { for (i = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
for (i = 0; i < ARR_GetSize(config_hwts_ifaces); i++) { if (strcmp("*", conf_iface->name))
if_name = *(char **)ARR_GetElement(config_hwts_ifaces, i); continue;
if (!add_interface(if_name)) if (add_all_interfaces(conf_iface))
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", if_name);
}
hwts = 1; hwts = 1;
} else if (wildcard && add_all_interfaces()) { break;
hwts = 1;
} else {
hwts = 0;
} }
if (hwts) {
ts_flags = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_HARDWARE;
} else {
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE; ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_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 */ /* Enable IP_PKTINFO in messages looped back to the error queue */
@@ -315,13 +380,13 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
flags |= ts_tx_flags; flags |= ts_tx_flags;
if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) { if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE"); LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
ts_flags = 0; ts_flags = 0;
return 0; return 0;
} }
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) { if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_TIMESTAMPING"); LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
ts_flags = 0; ts_flags = 0;
return 0; return 0;
} }
@@ -332,68 +397,6 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */ /* ================================================== */
static int
get_phc_sample(int phc_fd, struct timespec *phc_ts, struct timespec *local_ts, double *p_delay)
{
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, local_sum, local_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(LOGF_NtpIOLinux, "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? */
return 0;
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
local_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = n = 0, phc_sum = local_sum = 0.0; i < PHC_READINGS; i++) {
if (delays[i] > min_delay + local_prec)
continue;
phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
local_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], local_sum / n, &ts1);
LCL_CookTime(&ts1, local_ts, NULL);
*p_delay = min_delay;
return 1;
}
/* ================================================== */
static struct Interface * static struct Interface *
get_interface(int if_index) get_interface(int if_index)
{ {
@@ -415,28 +418,32 @@ get_interface(int if_index)
static void static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts, process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family) NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{ {
struct timespec sample_phc_ts, sample_local_ts; struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double sample_delay, rx_correction; double rx_correction, ts_delay, phc_err, local_err;
int l2_length;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) { if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!get_phc_sample(iface->phc_fd, &sample_phc_ts, &sample_local_ts, &sample_delay)) if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return; return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
sample_delay / 2.0); phc_err + local_err);
update_interface_speed(iface); update_interface_speed(iface);
} }
/* We need to transpose RX timestamps as hardware timestamps are normally /* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer 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 timestamps. If we don't know the length of the packet at layer 2, we
at layer 2, so we make an assumption that UDP data start at the same make an assumption that UDP data start at the same position as in the
position as in the last transmitted packet which had a HW TX timestamp. */ last transmitted packet which had a HW TX timestamp. */
if (rx_ntp_length && iface->link_speed) { if (rx_ntp_length && iface->link_speed) {
if (!l2_length)
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start : l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
iface->l2_udp6_ntp_start) + rx_ntp_length + 4; iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed); rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
@@ -444,9 +451,23 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts); UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
} }
if (!HCL_CookTime(iface->clock, hw_ts, &local_ts->ts, &local_ts->err)) if (!HCL_CookTime(iface->clock, hw_ts, &ts, &local_err))
return; 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; local_ts->source = NTP_TS_HARDWARE;
} }
@@ -517,34 +538,51 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
int int
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
int length, int sock_fd, int if_index)
{ {
struct Interface *iface; struct Interface *iface;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
int is_tx, l2_length; int is_tx, ts_if_index, l2_length;
is_tx = hdr->msg_flags & MSG_ERRQUEUE; is_tx = hdr->msg_flags & MSG_ERRQUEUE;
iface = NULL; iface = NULL;
ts_if_index = local_addr->if_index;
l2_length = 0;
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) { 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) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
struct scm_timestamping ts3; struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3)); memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
if (!UTI_IsZeroTimespec(&ts3.ts[0])) { if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err); iface = get_interface(ts_if_index);
local_ts->source = NTP_TS_KERNEL;
} else {
iface = get_interface(if_index);
if (iface) { if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0, process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
remote_addr->ip_addr.family); remote_addr->ip_addr.family, l2_length);
} else { } else {
DEBUG_LOG(LOGF_NtpIOLinux, "HW clock not found for interface %d", if_index); 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) || if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
@@ -555,7 +593,7 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND || if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
DEBUG_LOG(LOGF_NtpIOLinux, "Unknown extended error"); DEBUG_LOG("Unknown extended error");
/* Drop the message */ /* Drop the message */
return 1; return 1;
} }
@@ -572,9 +610,9 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
l2_length = length; l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length); length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
DEBUG_LOG(LOGF_NtpIOLinux, "Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d", 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, l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
sock_fd, if_index, local_ts->source); local_addr->sock_fd, local_addr->if_index, local_ts->source);
/* Update assumed position of UDP data at layer 2 for next received packet */ /* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && length) { if (iface && length) {
@@ -584,9 +622,9 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
iface->l2_udp6_ntp_start = l2_length - length; iface->l2_udp6_ntp_start = l2_length - length;
} }
/* Drop the message if HW timestamp is missing or its processing failed */ /* Drop the message if it has no timestamp or its processing failed */
if ((ts_flags & SOF_TIMESTAMPING_RAW_HARDWARE) && local_ts->source != NTP_TS_HARDWARE) { if (local_ts->source == NTP_TS_DAEMON) {
DEBUG_LOG(LOGF_NtpIOLinux, "Missing HW timestamp"); DEBUG_LOG("Missing TX timestamp");
return 1; return 1;
} }

View File

@@ -31,7 +31,6 @@ extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events); 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, 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, NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
int sock_fd, int if_index);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd); extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);

View File

@@ -135,7 +135,7 @@ open_socket(void)
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) { if (sock_fd < 0) {
DEBUG_LOG(LOGF_NtpSignd, "Could not open signd socket : %s", strerror(errno)); DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
return 0; return 0;
} }
@@ -145,18 +145,18 @@ open_socket(void)
s.sun_family = AF_UNIX; s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket", if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) { CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
DEBUG_LOG(LOGF_NtpSignd, "signd socket path too long"); DEBUG_LOG("signd socket path too long");
close_socket(); close_socket();
return 0; return 0;
} }
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) { if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
DEBUG_LOG(LOGF_NtpSignd, "Could not connect to signd : %s", strerror(errno)); DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
close_socket(); close_socket();
return 0; return 0;
} }
DEBUG_LOG(LOGF_NtpSignd, "Connected to signd"); DEBUG_LOG("Connected to signd");
return 1; return 1;
} }
@@ -170,25 +170,25 @@ process_response(SignInstance *inst)
double delay; double delay;
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) { if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid response ID"); DEBUG_LOG("Invalid response ID");
return; return;
} }
if (ntohl(inst->response.op) != SIGNING_SUCCESS) { if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
DEBUG_LOG(LOGF_NtpSignd, "Signing failed"); DEBUG_LOG("Signing failed");
return; return;
} }
/* Check if the file descriptor is still valid */ /* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) { if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid NTP socket"); DEBUG_LOG("Invalid NTP socket");
return; return;
} }
SCH_GetLastEventTime(NULL, NULL, &ts); SCH_GetLastEventTime(NULL, NULL, &ts);
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts); delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
DEBUG_LOG(LOGF_NtpSignd, "Signing succeeded (delay %f)", delay); DEBUG_LOG("Signing succeeded (delay %f)", delay);
/* Send the signed NTP packet */ /* Send the signed NTP packet */
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr, NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
@@ -222,12 +222,12 @@ read_write_socket(int sock_fd, int event, void *anything)
inst->request_length - inst->sent, 0); inst->request_length - inst->sent, 0);
if (s < 0) { if (s < 0) {
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno)); DEBUG_LOG("signd socket error: %s", strerror(errno));
close_socket(); close_socket();
return; return;
} }
DEBUG_LOG(LOGF_NtpSignd, "Sent %d bytes to signd", s); DEBUG_LOG("Sent %d bytes to signd", s);
inst->sent += s; inst->sent += s;
/* Try again later if the request is not complete yet */ /* Try again later if the request is not complete yet */
@@ -240,7 +240,7 @@ read_write_socket(int sock_fd, int event, void *anything)
if (event == SCH_FILE_INPUT) { if (event == SCH_FILE_INPUT) {
if (IS_QUEUE_EMPTY()) { if (IS_QUEUE_EMPTY()) {
DEBUG_LOG(LOGF_NtpSignd, "Unexpected signd response"); DEBUG_LOG("Unexpected signd response");
close_socket(); close_socket();
return; return;
} }
@@ -251,15 +251,15 @@ read_write_socket(int sock_fd, int event, void *anything)
if (s <= 0) { if (s <= 0) {
if (s < 0) if (s < 0)
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno)); DEBUG_LOG("signd socket error: %s", strerror(errno));
else else
DEBUG_LOG(LOGF_NtpSignd, "signd socket closed"); DEBUG_LOG("signd socket closed");
close_socket(); close_socket();
return; return;
} }
DEBUG_LOG(LOGF_NtpSignd, "Received %d bytes from signd", s); DEBUG_LOG("Received %d bytes from signd", s);
inst->received += s; inst->received += s;
if (inst->received < sizeof (inst->response.length)) if (inst->received < sizeof (inst->response.length))
@@ -269,7 +269,7 @@ read_write_socket(int sock_fd, int event, void *anything)
if (response_length < offsetof(SigndResponse, signed_packet) || if (response_length < offsetof(SigndResponse, signed_packet) ||
response_length > sizeof (SigndResponse)) { response_length > sizeof (SigndResponse)) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid response length"); DEBUG_LOG("Invalid response length");
close_socket(); close_socket();
return; return;
} }
@@ -303,7 +303,7 @@ NSD_Initialise()
ARR_SetSize(queue, MAX_QUEUE_LENGTH); ARR_SetSize(queue, MAX_QUEUE_LENGTH);
queue_head = queue_tail = 0; queue_head = queue_tail = 0;
LOG(LOGS_INFO, LOGF_NtpSignd, "MS-SNTP authentication enabled"); LOG(LOGS_INFO, "MS-SNTP authentication enabled");
} }
/* ================================================== */ /* ================================================== */
@@ -333,17 +333,17 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
SignInstance *inst; SignInstance *inst;
if (!enabled) { if (!enabled) {
DEBUG_LOG(LOGF_NtpSignd, "signd disabled"); DEBUG_LOG("signd disabled");
return 0; return 0;
} }
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) { if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
DEBUG_LOG(LOGF_NtpSignd, "signd queue full"); DEBUG_LOG("signd queue full");
return 0; return 0;
} }
if (length != NTP_NORMAL_PACKET_LENGTH) { if (length != NTP_NORMAL_PACKET_LENGTH) {
DEBUG_LOG(LOGF_NtpSignd, "Invalid packet length"); DEBUG_LOG("Invalid packet length");
return 0; return 0;
} }
@@ -373,8 +373,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
queue_tail = NEXT_QUEUE_INDEX(queue_tail); queue_tail = NEXT_QUEUE_INDEX(queue_tail);
DEBUG_LOG(LOGF_NtpSignd, "Packet added to signd queue (%u:%u)", DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
queue_head, queue_tail);
return 1; return 1;
} }

View File

@@ -39,6 +39,7 @@
#include "local.h" #include "local.h"
#include "memory.h" #include "memory.h"
#include "nameserv_async.h" #include "nameserv_async.h"
#include "privops.h"
#include "sched.h" #include "sched.h"
/* ================================================== */ /* ================================================== */
@@ -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 */ /* The hash table must be rebuilt for the new address */
rehash_records(); 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(&old_addr->ip_addr),
UTI_IPToString(&new_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.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
address.port = us->port; 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 (us->replacement) {
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse) 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); 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) { switch (status) {
case DNS_TryAgain: 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); process_resolved_name(us, ip_addrs, n_addrs);
break; break;
case DNS_Failure: case DNS_Failure:
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name); LOG(LOGS_WARN, "Invalid host %s", us->name);
break; break;
default: default:
assert(0); assert(0);
@@ -438,7 +439,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
if (next) { if (next) {
/* Continue with the next source in the list */ /* 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); DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
} else { } else {
/* This was the last source in the list. If some sources couldn't /* This was the last source in the list. If some sources couldn't
@@ -469,14 +470,14 @@ resolve_sources(void *arg)
assert(!resolving_source); assert(!resolving_source);
DNS_Reload(); PRV_ReloadDNS();
/* Start with the first source in the list, name_resolve_handler /* Start with the first source in the list, name_resolve_handler
will iterate over the rest */ will iterate over the rest */
us = unresolved_sources; us = unresolved_sources;
resolving_source = us; 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); DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
} }
@@ -657,8 +658,7 @@ resolve_source_replacement(SourceRecord *record)
{ {
struct UnresolvedSource *us; struct UnresolvedSource *us;
DEBUG_LOG(LOGF_NtpSources, "trying to replace %s", DEBUG_LOG("trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr));
UTI_IPToString(&record->remote_addr->ip_addr));
us = MallocNew(struct UnresolvedSource); us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name); us->name = Strdup(record->name);
@@ -704,7 +704,7 @@ NSR_HandleBadSource(IPAddr *address)
SCH_GetLastEventTime(NULL, NULL, &now); SCH_GetLastEventTime(NULL, NULL, &now);
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement); diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) { if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG(LOGF_NtpSources, "replacement postponed"); DEBUG_LOG("replacement postponed");
return; return;
} }
last_replacement = now; last_replacement = now;
@@ -742,7 +742,7 @@ static void remove_tentative_pool_sources(int pool)
if (!record->remote_addr || record->pool != pool || !record->tentative) if (!record->remote_addr || record->pool != pool || !record->tentative)
continue; continue;
DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s", DEBUG_LOG("removing tentative source %s",
UTI_IPToString(&record->remote_addr->ip_addr)); UTI_IPToString(&record->remote_addr->ip_addr));
clean_source_record(record); clean_source_record(record);
@@ -800,8 +800,7 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
pool = ARR_GetElement(pools, record->pool); pool = ARR_GetElement(pools, record->pool);
pool->sources++; pool->sources++;
DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources", DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources);
record->name, pool->sources);
/* If the number of sources from the pool reached the configured /* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */ maximum, remove the remaining tentative sources */

View File

@@ -123,7 +123,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(null), /* NULL */ RPY_LENGTH_ENTRY(null), /* NULL */
RPY_LENGTH_ENTRY(n_sources), /* N_SOURCES */ RPY_LENGTH_ENTRY(n_sources), /* N_SOURCES */
RPY_LENGTH_ENTRY(source_data), /* SOURCE_DATA */ 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(tracking), /* TRACKING */
RPY_LENGTH_ENTRY(sourcestats), /* SOURCESTATS */ RPY_LENGTH_ENTRY(sourcestats), /* SOURCESTATS */
RPY_LENGTH_ENTRY(rtc), /* RTC */ RPY_LENGTH_ENTRY(rtc), /* RTC */
@@ -136,6 +136,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */ RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */ 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_SETTIME 1026
#define OP_BINDSOCKET 1027 #define OP_BINDSOCKET 1027
#define OP_NAME2IPADDRESS 1028 #define OP_NAME2IPADDRESS 1028
#define OP_RELOADDNS 1029
#define OP_QUIT 1099 #define OP_QUIT 1099
union sockaddr_in46 { union sockaddr_in46 {
@@ -293,8 +294,6 @@ do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
/* make sure the string is terminated */ /* make sure the string is terminated */
req->name[sizeof (req->name) - 1] = '\0'; req->name[sizeof (req->name) - 1] = '\0';
DNS_Reload();
res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses, res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses,
DNS_MAX_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 */ /* HELPER - main loop - action requests from the daemon */
static void static void
@@ -343,6 +355,11 @@ helper_main(int fd)
case OP_NAME2IPADDRESS: case OP_NAME2IPADDRESS:
do_name_to_ipaddress(&req.data.name_to_ipaddress, &res); do_name_to_ipaddress(&req.data.name_to_ipaddress, &res);
break; break;
#endif
#ifdef PRIVOPS_RELOADDNS
case OP_RELOADDNS:
do_reload_dns(&res);
break;
#endif #endif
case OP_QUIT: case OP_QUIT:
quit = 1; quit = 1;
@@ -371,14 +388,14 @@ receive_response(PrvResponse *res)
resp_len = recv(helper_fd, res, sizeof (*res), 0); resp_len = recv(helper_fd, res, sizeof (*res), 0);
if (resp_len < 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)) if (resp_len != sizeof (*res))
LOG_FATAL(LOGF_PrivOps, "Invalid helper response"); LOG_FATAL("Invalid helper response");
if (res->fatal_error) 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 operation failed in the helper, set errno so daemon can print log message */
if (res->res_errno) if (res->res_errno)
@@ -429,10 +446,10 @@ send_request(PrvRequest *req)
if (sendmsg(helper_fd, &msg, 0) < 0) { if (sendmsg(helper_fd, &msg, 0) < 0) {
/* don't try to send another request from exit() */ /* don't try to send another request from exit() */
helper_fd = -1; 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; req.op = OP_NAME2IPADDRESS;
if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name), if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
"%s", 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; 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 void
PRV_Initialise(void) PRV_Initialise(void)
{ {
@@ -631,21 +672,21 @@ PRV_StartHelper(void)
int fd, sock_pair[2]; int fd, sock_pair[2];
if (have_helper()) if (have_helper())
LOG_FATAL(LOGF_PrivOps, "Helper already running"); LOG_FATAL("Helper already running");
if ( if (
#ifdef SOCK_SEQPACKET #ifdef SOCK_SEQPACKET
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) && socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) &&
#endif #endif
socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair)) 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[0]);
UTI_FdSetCloexec(sock_pair[1]); UTI_FdSetCloexec(sock_pair[1]);
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
LOG_FATAL(LOGF_PrivOps, "fork() failed : %s", strerror(errno)); LOG_FATAL("fork() failed : %s", strerror(errno));
if (pid == 0) { if (pid == 0) {
/* child process */ /* 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 #define PRV_Name2IPAddress DNS_Name2IPAddress
#endif #endif
#ifdef PRIVOPS_RELOADDNS
void PRV_ReloadDNS(void);
#else
#define PRV_ReloadDNS DNS_Reload
#endif
#ifdef PRIVOPS_HELPER #ifdef PRIVOPS_HELPER
void PRV_Initialise(void); void PRV_Initialise(void);
void PRV_StartHelper(void); void PRV_StartHelper(void);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -75,6 +75,7 @@ struct RCL_Instance_Record {
int driver_polled; int driver_polled;
int poll; int poll;
int leap_status; int leap_status;
int pps_forced;
int pps_rate; int pps_rate;
int pps_active; int pps_active;
int max_lock_age; int max_lock_age;
@@ -84,6 +85,7 @@ struct RCL_Instance_Record {
double offset; double offset;
double delay; double delay;
double precision; double precision;
double pulse_width;
SCH_TimeoutID timeout_id; SCH_TimeoutID timeout_id;
SRC_Instance source; SRC_Instance source;
}; };
@@ -93,7 +95,7 @@ static ARR_Instance refclocks;
static LOG_FileID logfileid; static LOG_FileID logfileid;
static int valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked); static int valid_sample_time(RCL_Instance instance, struct timespec *sample_time);
static int pps_stratum(RCL_Instance instance, struct timespec *ts); static int pps_stratum(RCL_Instance instance, struct timespec *ts);
static void poll_timeout(void *arg); static void poll_timeout(void *arg);
static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq, static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
@@ -164,7 +166,6 @@ RCL_Finalise(void)
int int
RCL_AddRefclock(RefclockParameters *params) RCL_AddRefclock(RefclockParameters *params)
{ {
int pps_source = 0;
RCL_Instance inst; RCL_Instance inst;
inst = MallocNew(struct RCL_Instance_Record); inst = MallocNew(struct RCL_Instance_Record);
@@ -172,25 +173,19 @@ RCL_AddRefclock(RefclockParameters *params)
if (strcmp(params->driver_name, "SHM") == 0) { if (strcmp(params->driver_name, "SHM") == 0) {
inst->driver = &RCL_SHM_driver; inst->driver = &RCL_SHM_driver;
inst->precision = 1e-6;
} else if (strcmp(params->driver_name, "SOCK") == 0) { } else if (strcmp(params->driver_name, "SOCK") == 0) {
inst->driver = &RCL_SOCK_driver; inst->driver = &RCL_SOCK_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PPS") == 0) { } else if (strcmp(params->driver_name, "PPS") == 0) {
inst->driver = &RCL_PPS_driver; inst->driver = &RCL_PPS_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PHC") == 0) { } else if (strcmp(params->driver_name, "PHC") == 0) {
inst->driver = &RCL_PHC_driver; inst->driver = &RCL_PHC_driver;
inst->precision = 1e-9;
} else { } else {
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name); LOG_FATAL("unknown refclock driver %s", params->driver_name);
return 0; return 0;
} }
if (!inst->driver->init && !inst->driver->poll) { 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; return 0;
} }
@@ -201,14 +196,16 @@ RCL_AddRefclock(RefclockParameters *params)
inst->poll = params->poll; inst->poll = params->poll;
inst->driver_polled = 0; inst->driver_polled = 0;
inst->leap_status = LEAP_Normal; inst->leap_status = LEAP_Normal;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate; inst->pps_rate = params->pps_rate;
inst->pps_active = 0; inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age; inst->max_lock_age = params->max_lock_age;
inst->lock_ref = params->lock_ref_id; inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset; inst->offset = params->offset;
inst->delay = params->delay; inst->delay = params->delay;
if (params->precision > 0.0) inst->precision = LCL_GetSysPrecisionAsQuantum();
inst->precision = params->precision; inst->precision = MAX(inst->precision, params->precision);
inst->pulse_width = params->pulse_width;
inst->timeout_id = -1; inst->timeout_id = -1;
inst->source = NULL; inst->source = NULL;
@@ -221,12 +218,8 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_parameter[i] = '\0'; inst->driver_parameter[i] = '\0';
} }
if (pps_source) {
if (inst->pps_rate < 1) if (inst->pps_rate < 1)
inst->pps_rate = 1; inst->pps_rate = 1;
} else {
inst->pps_rate = 0;
}
if (params->ref_id) if (params->ref_id)
inst->ref_id = params->ref_id; inst->ref_id = params->ref_id;
@@ -251,7 +244,7 @@ RCL_AddRefclock(RefclockParameters *params)
max_samples = 1 << (inst->poll - inst->driver_poll); max_samples = 1 << (inst->poll - inst->driver_poll);
if (max_samples < params->filter_length) { if (max_samples < params->filter_length) {
if (max_samples < 4) { 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); UTI_RefidToString(inst->ref_id), max_samples);
} }
params->filter_length = max_samples; params->filter_length = max_samples;
@@ -260,7 +253,7 @@ RCL_AddRefclock(RefclockParameters *params)
if (inst->driver->init) if (inst->driver->init)
if (!inst->driver->init(inst)) { 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; return 0;
} }
@@ -269,7 +262,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL, inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples); 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), params->driver_name, UTI_RefidToString(inst->ref_id),
inst->poll, inst->driver_poll, params->filter_length); inst->poll, inst->driver_poll, params->filter_length);
@@ -369,13 +362,16 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
double correction, dispersion; double correction, dispersion;
struct timespec cooked_time; struct timespec cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time, -offset);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion); LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time); UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision; dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */ /* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) || if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time, &cooked_time)) !valid_sample_time(instance, &cooked_time))
return 0; return 0;
switch (leap) { switch (leap) {
@@ -385,7 +381,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
instance->leap_status = leap; instance->leap_status = leap;
break; break;
default: default:
DEBUG_LOG(LOGF_Refclock, "refclock sample ignored bad leap %d", leap); DEBUG_LOG("refclock sample ignored bad leap %d", leap);
return 0; return 0;
} }
@@ -404,24 +400,57 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
int int
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second) RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
{ {
double correction, dispersion, offset; double correction, dispersion;
struct timespec cooked_time; 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; int rate;
NTP_Leap leap; NTP_Leap leap;
leap = LEAP_Normal; if (!UTI_IsTimeOffsetSane(cooked_time, second) ||
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion); !valid_sample_time(instance, cooked_time))
UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
!valid_sample_time(instance, pulse_time, &cooked_time))
return 0; return 0;
leap = LEAP_Normal;
dispersion += instance->precision;
rate = instance->pps_rate; 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 */ /* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
offset -= (long)(offset * rate) / (double)rate; offset -= (long)(offset * rate) / (double)rate;
@@ -439,15 +468,15 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
if (!filter_get_last_sample(&lock_refclock->filter, if (!filter_get_last_sample(&lock_refclock->filter,
&ref_sample_time, &ref_offset, &ref_dispersion)) { &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; return 0;
} }
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter); ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
sample_diff = UTI_DiffTimespecsToDouble(&cooked_time, &ref_sample_time); sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) { if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f", DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff); sample_diff);
return 0; return 0;
} }
@@ -461,15 +490,18 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
offset += shift; offset += shift;
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) { 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); ref_offset - offset, ref_dispersion, dispersion);
return 0; return 0;
} }
if (!check_pulse_edge(instance, ref_offset - offset, 0.0))
return 0;
leap = lock_refclock->leap_status; leap = lock_refclock->leap_status;
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f", DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff); offset, ref_offset - offset, sample_diff);
} else { } else {
struct timespec ref_time; struct timespec ref_time;
int is_synchronised, stratum; int is_synchronised, stratum;
@@ -479,24 +511,28 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
/* Ignore the pulse if we are not well synchronized and the local /* Ignore the pulse if we are not well synchronized and the local
reference is not active */ 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); &ref_id, &ref_time, &root_delay, &root_dispersion);
distance = fabs(root_delay) / 2 + root_dispersion; distance = fabs(root_delay) / 2 + root_dispersion;
if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) { if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f", DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
second, leap != LEAP_Unsynchronised, distance); offset, leap != LEAP_Unsynchronised, distance);
/* Drop also all stored samples */ /* Drop also all stored samples */
filter_reset(&instance->filter); filter_reset(&instance->filter);
return 0; 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->leap_status = leap;
instance->pps_active = 1; 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 */ /* for logging purposes */
if (!instance->driver->poll) if (!instance->driver->poll)
@@ -505,23 +541,35 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
return 1; return 1;
} }
static int double
valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked) RCL_GetPrecision(RCL_Instance instance)
{ {
struct timespec now_raw, 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; double diff, last_offset, last_dispersion;
LCL_ReadRawTime(&now_raw); LCL_ReadCookedTime(&now, NULL);
diff = UTI_DiffTimespecsToDouble(&now_raw, raw); diff = UTI_DiffTimespecsToDouble(&now, sample_time);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) || if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
(filter_get_samples(&instance->filter) > 0 && (filter_get_samples(&instance->filter) > 0 &&
filter_get_last_sample(&instance->filter, &last_sample_time, filter_get_last_sample(&instance->filter, &last_sample_time,
&last_offset, &last_dispersion) && &last_offset, &last_dispersion) &&
UTI_CompareTimespecs(&last_sample_time, cooked) >= 0)) { UTI_CompareTimespecs(&last_sample_time, sample_time) >= 0)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f raw=%s cooked=%s", DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
UTI_RefidToString(instance->ref_id), diff, UTI_RefidToString(instance->ref_id),
UTI_TimespecToString(raw), UTI_TimespecToString(cooked)); UTI_TimespecToString(sample_time), diff);
return 0; return 0;
} }
@@ -713,7 +761,7 @@ filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, dou
filter->samples[filter->index].offset = offset; filter->samples[filter->index].offset = offset;
filter->samples[filter->index].dispersion = dispersion; filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f", DEBUG_LOG("filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimespecToString(sample_time), offset, dispersion); filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
} }
@@ -903,7 +951,7 @@ filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, dou
/* drop the sample if variance is larger than allowed maximum */ /* drop the sample if variance is larger than allowed maximum */
if (filter->max_var > 0.0 && var > filter->max_var) { 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)); sqrt(var), sqrt(filter->max_var));
return 0; return 0;
} }

View File

@@ -37,6 +37,7 @@ typedef struct {
int driver_poll; int driver_poll;
int poll; int poll;
int filter_length; int filter_length;
int pps_forced;
int pps_rate; int pps_rate;
int min_samples; int min_samples;
int max_samples; int max_samples;
@@ -48,6 +49,7 @@ typedef struct {
double delay; double delay;
double precision; double precision;
double max_dispersion; double max_dispersion;
double pulse_width;
} RefclockParameters; } RefclockParameters;
typedef struct RCL_Instance_Record *RCL_Instance; typedef struct RCL_Instance_Record *RCL_Instance;
@@ -71,5 +73,9 @@ extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name); extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap); 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_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 #endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -33,144 +33,134 @@
#include "sysincl.h" #include "sysincl.h"
#include <linux/ptp_clock.h>
#include "refclock.h" #include "refclock.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h" #include "logging.h"
#include "memory.h"
#include "util.h" #include "util.h"
#include "sched.h"
#include "sys_linux.h"
/* From linux/include/linux/posix-timers.h */ struct phc_instance {
#define CPUCLOCK_MAX 3 int fd;
#define CLOCKFD CPUCLOCK_MAX int mode;
#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK) int nocrossts;
int extpps;
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) int pin;
int channel;
#define NUM_READINGS 10 HCL_Instance clock;
static int no_sys_offset_ioctl = 0;
struct phc_reading {
struct timespec sys_ts1;
struct timespec phc_ts;;
struct timespec sys_ts2;
}; };
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n) static void read_ext_pulse(int sockfd, int event, void *anything);
{
#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 int phc_initialise(RCL_Instance instance) static int phc_initialise(RCL_Instance instance)
{ {
struct ptp_clock_caps caps; struct phc_instance *phc;
int phc_fd; int phc_fd, rising_edge;
char *path; char *path, *s;
path = RCL_GetDriverParameter(instance); path = RCL_GetDriverParameter(instance);
phc_fd = open(path, O_RDONLY); phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0) { if (phc_fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path); LOG_FATAL("Could not open PHC");
return 0; return 0;
} }
/* Make sure it is a PHC */ phc = MallocNew(struct phc_instance);
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) { phc->fd = phc_fd;
LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno)); phc->mode = 0;
return 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, phc);
RCL_SetDriverData(instance, (void *)(long)phc_fd);
return 1; return 1;
} }
static void phc_finalise(RCL_Instance instance) 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) static int phc_poll(RCL_Instance instance)
{ {
struct phc_reading readings[NUM_READINGS]; struct phc_instance *phc;
double offset = 0.0, delay, best_delay = 0.0; struct timespec phc_ts, sys_ts, local_ts;
int i, phc_fd, best; double offset, phc_err, local_err;
phc_fd = (long)RCL_GetDriverData(instance); phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!no_sys_offset_ioctl) { if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) { &phc->mode, &phc_ts, &sys_ts, &phc_err))
no_sys_offset_ioctl = 1;
return 0; return 0;
}
} else { if (phc->extpps) {
if (!read_phc_user(readings, phc_fd, NUM_READINGS)) LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
return 0; return 0;
} }
/* Find the fastest reading */ offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
for (i = 0; i < NUM_READINGS; i++) {
delay = UTI_DiffTimespecsToDouble(&readings[i].sys_ts2, &readings[i].sys_ts1);
if (!i || best_delay > delay) { DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
best = i;
best_delay = delay;
}
}
offset = UTI_DiffTimespecsToDouble(&readings[best].phc_ts, &readings[best].sys_ts2) + return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
best_delay / 2.0;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
return RCL_AddSample(instance, &readings[best].sys_ts2, offset, LEAP_Normal);
} }
RefclockDriver RCL_PHC_driver = { RefclockDriver RCL_PHC_driver = {

View File

@@ -59,37 +59,37 @@ static int pps_initialise(RCL_Instance instance) {
fd = open(path, O_RDWR); fd = open(path, O_RDWR);
if (fd < 0) { if (fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path); LOG_FATAL("open() failed on %s", path);
return 0; return 0;
} }
UTI_FdSetCloexec(fd); UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) { 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; return 0;
} }
if (time_pps_getcap(handle, &mode) < 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; return 0;
} }
if (time_pps_getparams(handle, &params) < 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; return 0;
} }
if (!edge_clear) { if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT)) { if (!(mode & PPS_CAPTUREASSERT)) {
LOG_FATAL(LOGF_Refclock, "CAPTUREASSERT not supported on %s", path); LOG_FATAL("CAPTUREASSERT not supported on %s", path);
return 0; return 0;
} }
params.mode |= PPS_CAPTUREASSERT; params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR; params.mode &= ~PPS_CAPTURECLEAR;
} else { } else {
if (!(mode & PPS_CAPTURECLEAR)) { if (!(mode & PPS_CAPTURECLEAR)) {
LOG_FATAL(LOGF_Refclock, "CAPTURECLEAR not supported on %s", path); LOG_FATAL("CAPTURECLEAR not supported on %s", path);
return 0; return 0;
} }
params.mode |= PPS_CAPTURECLEAR; params.mode |= PPS_CAPTURECLEAR;
@@ -97,7 +97,7 @@ static int pps_initialise(RCL_Instance instance) {
} }
if (time_pps_setparams(handle, &params) < 0) { 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; return 0;
} }
@@ -133,7 +133,7 @@ static int pps_poll(RCL_Instance instance)
ts.tv_nsec = 0; ts.tv_nsec = 0;
if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 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; return 0;
} }
@@ -146,7 +146,7 @@ static int pps_poll(RCL_Instance instance)
} }
if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) { if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%s", DEBUG_LOG("PPS sample ignored seq=%lu ts=%s",
seq, UTI_TimespecToString(&ts)); seq, UTI_TimespecToString(&ts));
return 0; return 0;
} }

View File

@@ -69,13 +69,13 @@ static int shm_initialise(RCL_Instance instance) {
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm); id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
if (id == -1) { if (id == -1) {
LOG_FATAL(LOGF_Refclock, "shmget() failed"); LOG_FATAL("shmget() failed");
return 0; return 0;
} }
shm = (struct shmTime *)shmat(id, 0, 0); shm = (struct shmTime *)shmat(id, 0, 0);
if ((long)shm == -1) { if ((long)shm == -1) {
LOG_FATAL(LOGF_Refclock, "shmat() failed"); LOG_FATAL("shmat() failed");
return 0; return 0;
} }
@@ -100,7 +100,7 @@ static int shm_poll(RCL_Instance instance)
if ((t.mode == 1 && t.count != shm->count) || if ((t.mode == 1 && t.count != shm->count) ||
!(t.mode == 0 || t.mode == 1) || !t.valid) { !(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); t.mode, t.count, t.valid);
return 0; return 0;
} }

View File

@@ -69,19 +69,19 @@ static void read_sample(int sockfd, int event, void *anything)
s = recv(sockfd, &sample, sizeof (sample), 0); s = recv(sockfd, &sample, sizeof (sample), 0);
if (s < 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)); strerror(errno));
return; return;
} }
if (s != sizeof (sample)) { 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)); s, (long)sizeof (sample));
return; return;
} }
if (sample.magic != SOCK_MAGIC) { 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); sample.magic, SOCK_MAGIC);
return; return;
} }
@@ -106,13 +106,13 @@ static int sock_initialise(RCL_Instance instance)
s.sun_family = AF_UNIX; s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) { 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; return 0;
} }
sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0) { if (sockfd < 0) {
LOG_FATAL(LOGF_Refclock, "socket() failed"); LOG_FATAL("socket() failed");
return 0; return 0;
} }
@@ -120,7 +120,7 @@ static int sock_initialise(RCL_Instance instance)
unlink(path); unlink(path);
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) { if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
LOG_FATAL(LOGF_Refclock, "bind() failed"); LOG_FATAL("bind() failed");
return 0; return 0;
} }

View File

@@ -49,6 +49,7 @@ static int local_orphan;
static double local_distance; static double local_distance;
static NTP_Leap our_leap_status; static NTP_Leap our_leap_status;
static int our_leap_sec; static int our_leap_sec;
static int our_tai_offset;
static int our_stratum; static int our_stratum;
static uint32_t our_ref_id; static uint32_t our_ref_id;
static IPAddr our_ref_ip; static IPAddr our_ref_ip;
@@ -111,8 +112,6 @@ static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */ /* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname; static char *leap_tzname;
static time_t last_tz_leap_check;
static NTP_Leap tz_leap;
/* ================================================== */ /* ================================================== */
@@ -141,7 +140,7 @@ 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 update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */ /* ================================================== */
@@ -181,11 +180,13 @@ REF_Initialise(void)
FILE *in; FILE *in;
double file_freq_ppm, file_skew_ppm; double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm; double our_frequency_ppm;
int tai_offset;
mode = REF_ModeNormal; mode = REF_ModeNormal;
are_we_synchronised = 0; are_we_synchronised = 0;
our_leap_status = LEAP_Unsynchronised; our_leap_status = LEAP_Unsynchronised;
our_leap_sec = 0; our_leap_sec = 0;
our_tai_offset = 0;
initialised = 1; initialised = 1;
our_root_dispersion = 1.0; our_root_dispersion = 1.0;
our_root_delay = 1.0; our_root_delay = 1.0;
@@ -205,11 +206,11 @@ REF_Initialise(void)
our_skew = 1.0e-6 * file_skew_ppm; our_skew = 1.0e-6 * file_skew_ppm;
if (our_skew < MIN_SKEW) if (our_skew < MIN_SKEW)
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); file_freq_ppm, file_skew_ppm, drift_file);
LCL_SetAbsoluteFrequency(our_frequency_ppm); LCL_SetAbsoluteFrequency(our_frequency_ppm);
} else { } 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); drift_file);
} }
fclose(in); fclose(in);
@@ -219,7 +220,7 @@ REF_Initialise(void)
if (our_frequency_ppm == 0.0) { if (our_frequency_ppm == 0.0) {
our_frequency_ppm = LCL_ReadAbsoluteFrequency(); our_frequency_ppm = LCL_ReadAbsoluteFrequency();
if (our_frequency_ppm != 0.0) { 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);
} }
} }
@@ -243,11 +244,11 @@ REF_Initialise(void)
leap_tzname = CNF_GetLeapSecTimezone(); leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) { if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */ /* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
if (get_tz_leap(1341014400) == LEAP_InsertSecond && if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
get_tz_leap(1356912000) == LEAP_Normal) { get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname); LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
} else { } 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; leap_tzname = NULL;
} }
} }
@@ -364,7 +365,7 @@ update_drift_file(double freq_ppm, double skew)
out = fopen(temp_drift_file, "w"); out = fopen(temp_drift_file, "w");
if (!out) { if (!out) {
Free(temp_drift_file); 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); drift_file);
return; return;
} }
@@ -374,7 +375,7 @@ update_drift_file(double freq_ppm, double skew)
r2 = fclose(out); r2 = fclose(out);
if (r1 < 0 || r2) { if (r1 < 0 || r2) {
Free(temp_drift_file); 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); drift_file);
return; return;
} }
@@ -384,8 +385,7 @@ update_drift_file(double freq_ppm, double skew)
if (!stat(drift_file,&buf)) { if (!stat(drift_file,&buf)) {
if (chown(temp_drift_file,buf.st_uid,buf.st_gid) || if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
chmod(temp_drift_file,buf.st_mode & 0777)) { chmod(temp_drift_file,buf.st_mode & 0777)) {
LOG(LOGS_WARN, LOGF_Reference, LOG(LOGS_WARN, "Could not change ownership or permissions of temporary driftfile %s.tmp",
"Could not change ownership or permissions of temporary driftfile %s.tmp",
drift_file); drift_file);
} }
} }
@@ -395,7 +395,7 @@ update_drift_file(double freq_ppm, double skew)
if (rename(temp_drift_file,drift_file)) { if (rename(temp_drift_file,drift_file)) {
unlink(temp_drift_file); unlink(temp_drift_file);
Free(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); drift_file,drift_file);
return; return;
} }
@@ -443,7 +443,7 @@ update_fb_drifts(double freq_ppm, double update_interval)
(freq_ppm - fb_drifts[i].freq); (freq_ppm - fb_drifts[i].freq);
} }
DEBUG_LOG(LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds", DEBUG_LOG("Fallback drift %d updated: %f ppm %f seconds",
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs); i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
} }
} }
@@ -457,7 +457,7 @@ fb_drift_timeout(void *arg)
fb_drift_timeout_id = 0; 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); next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq);
LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq); LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq);
REF_SetUnsynchronised(); REF_SetUnsynchronised();
@@ -492,14 +492,14 @@ schedule_fb_drift(struct timespec *now)
if (c > next_fb_drift) { if (c > next_fb_drift) {
LCL_SetAbsoluteFrequency(fb_drifts[c - fb_drift_min].freq); LCL_SetAbsoluteFrequency(fb_drifts[c - fb_drift_min].freq);
next_fb_drift = c; 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) { if (i <= fb_drift_max) {
next_fb_drift = i; next_fb_drift = i;
UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when); UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL); 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);
} }
} }
@@ -531,8 +531,7 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset); abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) { if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, LOGF_Reference, LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
"System clock wrong by %.6f seconds, adjustment started",
-offset); -offset);
} }
@@ -558,8 +557,7 @@ maybe_log_offset(double offset, time_t now)
-offset, mail_change_threshold); -offset, mail_change_threshold);
pclose(p); pclose(p);
} else { } else {
LOG(LOGS_ERR, LOGF_Reference, LOG(LOGS_ERR, "Could not send mail notification to user %s\n",
"Could not send mail notification to user %s\n",
mail_change_user); mail_change_user);
} }
} }
@@ -594,7 +592,7 @@ is_offset_ok(double offset)
offset = fabs(offset); offset = fabs(offset);
if (offset > max_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) ", "Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored"); -offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
if (!max_offset_ignore) if (!max_offset_ignore)
@@ -618,12 +616,18 @@ is_leap_second_day(struct tm *stm) {
/* ================================================== */ /* ================================================== */
static NTP_Leap 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; struct tm stm;
time_t t; time_t t;
char *tz_env, tz_orig[128]; char *tz_env, tz_orig[128];
*tai_offset = tz_tai_offset;
/* Do this check at most twice a day */ /* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600); when = when / (12 * 3600) * (12 * 3600);
if (last_tz_leap_check == when) if (last_tz_leap_check == when)
@@ -631,12 +635,10 @@ get_tz_leap(time_t when)
last_tz_leap_check = when; last_tz_leap_check = when;
tz_leap = LEAP_Normal; tz_leap = LEAP_Normal;
tz_tai_offset = 0;
stm = *gmtime(&when); stm = *gmtime(&when);
if (!is_leap_second_day(&stm))
return tz_leap;
/* Temporarily switch to the timezone containing leap seconds */ /* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ"); tz_env = getenv("TZ");
if (tz_env) { if (tz_env) {
@@ -647,6 +649,11 @@ get_tz_leap(time_t when)
setenv("TZ", leap_tzname, 1); setenv("TZ", leap_tzname, 1);
tzset(); 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() */ /* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60; stm.tm_sec = 60;
stm.tm_min = 59; stm.tm_min = 59;
@@ -668,6 +675,8 @@ get_tz_leap(time_t when)
else if (stm.tm_sec == 1) else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond; tz_leap = LEAP_DeleteSecond;
*tai_offset = tz_tai_offset;
return tz_leap; return tz_leap;
} }
@@ -678,10 +687,13 @@ leap_end_timeout(void *arg)
{ {
leap_timeout_id = 0; leap_timeout_id = 0;
leap_in_progress = 0; leap_in_progress = 0;
if (our_tai_offset)
our_tai_offset += our_leap_sec;
our_leap_sec = 0; our_leap_sec = 0;
if (leap_mode == REF_LeapModeSystem) if (leap_mode == REF_LeapModeSystem)
LCL_SetSystemLeap(0); LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
if (our_leap_status == LEAP_InsertSecond || if (our_leap_status == LEAP_InsertSecond ||
our_leap_status == LEAP_DeleteSecond) our_leap_status == LEAP_DeleteSecond)
@@ -697,20 +709,20 @@ leap_start_timeout(void *arg)
switch (leap_mode) { switch (leap_mode) {
case REF_LeapModeSystem: case REF_LeapModeSystem:
DEBUG_LOG(LOGF_Reference, "Waiting for system clock leap second correction"); DEBUG_LOG("Waiting for system clock leap second correction");
break; break;
case REF_LeapModeSlew: case REF_LeapModeSlew:
LCL_NotifyLeap(our_leap_sec); LCL_NotifyLeap(our_leap_sec);
LCL_AccumulateOffset(our_leap_sec, 0.0); 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; break;
case REF_LeapModeStep: case REF_LeapModeStep:
LCL_NotifyLeap(our_leap_sec); LCL_NotifyLeap(our_leap_sec);
LCL_ApplyStepOffset(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; break;
case REF_LeapModeIgnore: case REF_LeapModeIgnore:
LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second"); LOG(LOGS_WARN, "Ignoring leap second");
break; break;
default: default:
break; break;
@@ -755,12 +767,17 @@ set_leap_timeout(time_t now)
static void static void
update_leap_status(NTP_Leap leap, time_t now, int reset) 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; leap_sec = 0;
tai_offset = 0;
if (leap_tzname && now && leap == LEAP_Normal) if (leap_tzname && now) {
leap = get_tz_leap(now); tz_leap = get_tz_leap(now, &tai_offset);
if (leap == LEAP_Normal)
leap = tz_leap;
}
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) { if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
/* Check that leap second is allowed today */ /* Check that leap second is allowed today */
@@ -776,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_leap_sec = leap_sec;
our_tai_offset = tai_offset;
switch (leap_mode) { switch (leap_mode) {
case REF_LeapModeSystem: case REF_LeapModeSystem:
LCL_SetSystemLeap(our_leap_sec); LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
/* Fall through */ /* Fall through */
case REF_LeapModeSlew: case REF_LeapModeSlew:
case REF_LeapModeStep: case REF_LeapModeStep:
@@ -825,15 +844,14 @@ special_mode_sync(int valid, double offset)
switch (mode) { switch (mode) {
case REF_ModeInitStepSlew: case REF_ModeInitStepSlew:
if (!valid) { if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew"); LOG(LOGS_WARN, "No suitable source for initstepslew");
end_ref_mode(0); end_ref_mode(0);
break; break;
} }
step = fabs(offset) >= CNF_GetInitStepThreshold(); step = fabs(offset) >= CNF_GetInitStepThreshold();
LOG(LOGS_INFO, LOGF_Reference, LOG(LOGS_INFO, "System's initial offset : %.6f seconds %s of true (%s)",
"System's initial offset : %.6f seconds %s of true (%s)",
fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew"); fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew");
if (step) if (step)
@@ -847,14 +865,14 @@ special_mode_sync(int valid, double offset)
case REF_ModeUpdateOnce: case REF_ModeUpdateOnce:
case REF_ModePrintOnce: case REF_ModePrintOnce:
if (!valid) { if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for synchronisation"); LOG(LOGS_WARN, "No suitable source for synchronisation");
end_ref_mode(0); end_ref_mode(0);
break; break;
} }
step = mode == REF_ModeUpdateOnce; 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"); -offset, step ? "step" : "ignored");
if (step) if (step)
@@ -928,7 +946,7 @@ REF_SetReference(int stratum,
double t; double t;
t = (skew + skew) / skew; /* Skew shouldn't be zero either */ t = (skew + skew) / skew; /* Skew shouldn't be zero either */
if ((t < 1.9) || (t > 2.1)) { if ((t < 1.9) || (t > 2.1)) {
LOG(LOGS_WARN, LOGF_Reference, "Bogus skew value encountered"); LOG(LOGS_WARN, "Bogus skew value encountered");
return; return;
} }
} }
@@ -1025,7 +1043,7 @@ REF_SetReference(int stratum,
LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate); LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
} else { } 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); LCL_AccumulateOffset(accumulate_offset, correction_rate);
@@ -1037,7 +1055,7 @@ REF_SetReference(int stratum,
if (step_offset != 0.0) { if (step_offset != 0.0) {
if (LCL_ApplyStepOffset(step_offset)) 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); LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
@@ -1214,7 +1232,7 @@ REF_GetReferenceParams
*leap_status = LEAP_Normal; *leap_status = LEAP_Normal;
*root_delay = 0.0; *root_delay = 0.0;
*root_dispersion = LCL_GetSysPrecisionAsQuantum(); *root_dispersion = 0.0;
} else { } else {
@@ -1348,7 +1366,7 @@ REF_GetTrackingReport(RPT_TrackingReport *rep)
&rep->ref_id, &rep->ref_time, &rep->ref_id, &rep->ref_time,
&rep->root_delay, &rep->root_dispersion); &rep->root_delay, &rep->root_dispersion);
if (rep->stratum == NTP_MAX_STRATUM) if (rep->stratum == NTP_MAX_STRATUM && !synchronised)
rep->stratum = 0; rep->stratum = 0;
rep->ip_addr.family = IPADDR_UNSPEC; rep->ip_addr.family = IPADDR_UNSPEC;

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,7 @@
#include "logging.h" #include "logging.h"
#include "util.h" #include "util.h"
#define MAX_POINTS 128 #define MAX_POINTS 64
void void
RGR_WeightedRegression RGR_WeightedRegression
@@ -285,7 +285,7 @@ RGR_FindBestRegression
n - start <= min_samples) { n - start <= min_samples) {
if (start != resid_start) { if (start != resid_start) {
/* Ignore extra samples in returned nruns */ /* 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; break;
} else { } else {
@@ -340,7 +340,7 @@ RGR_FindBestRegression
0-521-43108-5). */ 0-521-43108-5). */
static double 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; int u, v, l, r;
double temp; double temp;
@@ -403,9 +403,9 @@ find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
static double static double
find_ordered_entry(double *x, int n, int index) 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); return find_ordered_entry_with_flags(x, n, index, flags);
} }
#endif #endif
@@ -417,9 +417,9 @@ static double
find_median(double *x, int n) find_median(double *x, int n)
{ {
int k; 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; k = n>>1;
if (n&1) { if (n&1) {
return find_ordered_entry_with_flags(x, n, k, flags); return find_ordered_entry_with_flags(x, n, k, flags);
@@ -429,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 /* This function evaluates the equation
@@ -509,7 +522,7 @@ RGR_FindBestRobustRegression
double mx, dx, my, dy; double mx, dx, my, dy;
int nruns = 0; int nruns = 0;
assert(n < MAX_POINTS); assert(n <= MAX_POINTS);
if (n < 2) { if (n < 2) {
return 0; return 0;
@@ -566,23 +579,17 @@ RGR_FindBestRobustRegression
Estimate standard deviation of b and expand range about b based Estimate standard deviation of b and expand range about b based
on that. */ on that. */
sb = sqrt(s2 * W/V); sb = sqrt(s2 * W/V);
if (sb > tol) { incr = MAX(sb, tol);
incr = 3.0 * sb;
} else {
incr = 3.0 * tol;
}
blo = b;
bhi = b;
do { do {
/* Make sure incr is significant to blo and bhi */ incr *= 2.0;
while (bhi + incr == bhi || blo - incr == blo) {
incr *= 2;
}
blo -= incr; /* Give up if the interval is too large */
bhi += incr; if (incr > 100.0)
return 0;
blo = b - incr;
bhi = b + incr;
/* We don't want 'a' yet */ /* 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, blo, &a, &rlo);
@@ -594,6 +601,8 @@ RGR_FindBestRobustRegression
/* OK, so the root for b lies in (blo, bhi). Start bisecting */ /* OK, so the root for b lies in (blo, bhi). Start bisecting */
do { do {
bmid = 0.5 * (blo + bhi); bmid = 0.5 * (blo + bhi);
if (!(blo < bmid && bmid < bhi))
break;
eval_robust_residual(x + start, y + start, n_points, bmid, &a, &rmid); eval_robust_residual(x + start, y + start, n_points, bmid, &a, &rmid);
if (rmid == 0.0) { if (rmid == 0.0) {
break; break;
@@ -606,7 +615,7 @@ RGR_FindBestRobustRegression
} else { } else {
assert(0); assert(0);
} }
} while ((bhi - blo) > tol && (bmid - blo) * (bhi - bmid) > 0.0); } while (bhi - blo > tol);
*b0 = a; *b0 = a;
*b1 = bmid; *b1 = bmid;

View File

@@ -131,4 +131,7 @@ RGR_MultipleRegress
double *b2 /* estimated second slope */ double *b2 /* estimated second slope */
); );
/* Return the median value from an array */
extern double RGR_FindMedian(double *x, int n);
#endif /* GOT_REGRESS_H */ #endif /* GOT_REGRESS_H */

6
rtc.c
View File

@@ -104,7 +104,7 @@ apply_driftfile_time(time_t t)
if (now.tv_sec < t) { if (now.tv_sec < t) {
if (LCL_ApplyStepOffset(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 (file_name) {
if (CNF_GetRtcSync()) { 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) { if (driver.init) {
@@ -150,7 +150,7 @@ RTC_Initialise(int initial_set)
driver_initialised = 1; driver_initialised = 1;
} }
} else { } 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

@@ -187,6 +187,11 @@ accumulate_sample(time_t rtc, struct timespec *sys)
discard_samples(NEW_FIRST_WHEN_FULL); 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 */ /* Always use most recent sample as reference */
/* use sample only if n_sample is not negative*/ /* use sample only if n_sample is not negative*/
@@ -289,7 +294,7 @@ slew_samples
coef_gain_rate += dfreq * (1.0 - coef_gain_rate); 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, dfreq, doffset,
old_seconds_fast, 1.0e6 * old_gain_rate, old_seconds_fast, 1.0e6 * old_gain_rate,
coef_seconds_fast, 1.0e6 * coef_gain_rate); coef_seconds_fast, 1.0e6 * coef_gain_rate);
@@ -366,7 +371,7 @@ t_from_rtc(struct tm *stm) {
diff = t2 - t1; diff = t2 - t1;
if (t1 - diff == -1) if (t1 - diff == -1)
DEBUG_LOG(LOGF_RtcLinux, "Could not convert RTC time"); DEBUG_LOG("Could not convert RTC time");
return t1 - diff; return t1 - diff;
} }
@@ -385,7 +390,7 @@ read_hwclock_file(const char *hwclock_file)
in = fopen(hwclock_file, "r"); in = fopen(hwclock_file, "r");
if (!in) { if (!in) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open %s : %s", LOG(LOGS_WARN, "Could not open %s : %s",
hwclock_file, strerror(errno)); hwclock_file, strerror(errno));
return; return;
} }
@@ -403,8 +408,7 @@ read_hwclock_file(const char *hwclock_file)
} else if (i == 3 && !strncmp(line, "UTC", 3)) { } else if (i == 3 && !strncmp(line, "UTC", 3)) {
rtc_on_utc = 1; rtc_on_utc = 1;
} else { } else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read RTC LOCAL/UTC setting from %s", LOG(LOGS_WARN, "Could not read RTC LOCAL/UTC setting from %s", hwclock_file);
hwclock_file);
} }
} }
@@ -446,8 +450,7 @@ read_coefs_from_file(void)
&file_ref_offset, &file_ref_offset,
&file_rate_ppm) == 4) { &file_rate_ppm) == 4) {
} else { } else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from %s", LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
coefs_file_name);
} }
fclose(in); fclose(in);
} }
@@ -480,7 +483,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
out = fopen(temp_coefs_file_name, "w"); out = fopen(temp_coefs_file_name, "w");
if (!out) { if (!out) {
Free(temp_coefs_file_name); 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); coefs_file_name);
return RTC_ST_BADFILE; return RTC_ST_BADFILE;
} }
@@ -491,7 +494,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
r2 = fclose(out); r2 = fclose(out);
if (r1 < 0 || r2) { if (r1 < 0 || r2) {
Free(temp_coefs_file_name); 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); coefs_file_name);
return RTC_ST_BADFILE; return RTC_ST_BADFILE;
} }
@@ -501,7 +504,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
if (!stat(coefs_file_name,&buf)) { if (!stat(coefs_file_name,&buf)) {
if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) || if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) ||
chmod(temp_coefs_file_name,buf.st_mode & 0777)) { 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", "Could not change ownership or permissions of temporary RTC file %s.tmp",
coefs_file_name); coefs_file_name);
} }
@@ -512,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)) { if (rename(temp_coefs_file_name,coefs_file_name)) {
unlink(temp_coefs_file_name); unlink(temp_coefs_file_name);
Free(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); coefs_file_name, coefs_file_name);
return RTC_ST_BADFILE; return RTC_ST_BADFILE;
} }
@@ -545,7 +548,7 @@ RTC_Linux_Initialise(void)
fd = open (CNF_GetRtcDevice(), O_RDWR); fd = open (CNF_GetRtcDevice(), O_RDWR);
if (fd < 0) { 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)); CNF_GetRtcDevice(), strerror(errno));
return 0; return 0;
} }
@@ -606,16 +609,14 @@ switch_interrupts(int onoff)
if (onoff) { if (onoff) {
status = ioctl(fd, RTC_UIE_ON, 0); status = ioctl(fd, RTC_UIE_ON, 0);
if (status < 0) { if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s", LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "enable", strerror(errno));
"enable", strerror(errno));
return; return;
} }
skip_interrupts = 1; skip_interrupts = 1;
} else { } else {
status = ioctl(fd, RTC_UIE_OFF, 0); status = ioctl(fd, RTC_UIE_OFF, 0);
if (status < 0) { if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s", LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "disable", strerror(errno));
"disable", strerror(errno));
return; return;
} }
} }
@@ -653,7 +654,7 @@ set_rtc(time_t new_rtc_time)
status = ioctl(fd, RTC_SET_TIME, &rtc_raw); status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
if (status < 0) { if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not set RTC time"); LOG(LOGS_ERR, "Could not set RTC time");
} }
} }
@@ -696,10 +697,10 @@ handle_initial_trim(void)
sys_error_now = rtc_error_now - coef_seconds_fast; sys_error_now = rtc_error_now - coef_seconds_fast;
LCL_AccumulateOffset(sys_error_now, 0.0); 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); sys_error_now);
} else { } else {
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid rtcfile coefficients"); LOG(LOGS_WARN, "No valid rtcfile coefficients");
} }
coefs_valid = 0; coefs_valid = 0;
@@ -724,7 +725,7 @@ handle_relock_after_trim(void)
if (valid) { if (valid) {
write_coefs_to_file(1,ref,fast,saved_coef_gain_rate); write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
} else { } else {
DEBUG_LOG(LOGF_RtcLinux, "Could not do regression after trim"); DEBUG_LOG("Could not do regression after trim");
} }
coefs_valid = 0; coefs_valid = 0;
@@ -819,7 +820,7 @@ read_from_device(int fd_, int event, void *any)
if (status < 0) { if (status < 0) {
/* This looks like a bad error : the file descriptor was indicating it was /* This looks like a bad error : the file descriptor was indicating it was
* ready to read but we couldn't read anything. Give up. */ * 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)); LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
SCH_RemoveFileHandler(fd); SCH_RemoveFileHandler(fd);
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */ switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
close(fd); close(fd);
@@ -843,7 +844,7 @@ read_from_device(int fd_, int event, void *any)
status = ioctl(fd, RTC_RD_TIME, &rtc_raw); status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status < 0) { 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; error = 1;
goto turn_off_interrupt; goto turn_off_interrupt;
} }
@@ -884,7 +885,7 @@ turn_off_interrupt:
switch (operating_mode) { switch (operating_mode) {
case OM_INITIAL: case OM_INITIAL:
if (error) { 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; operating_mode = OM_NORMAL;
(after_init_hook)(after_init_hook_arg); (after_init_hook)(after_init_hook_arg);
@@ -897,7 +898,7 @@ turn_off_interrupt:
case OM_AFTERTRIM: case OM_AFTERTRIM:
if (error) { 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; operating_mode = OM_NORMAL;
switch_interrupts(0); switch_interrupts(0);
@@ -1036,7 +1037,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
UTI_AddDoubleToTimespec(&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) { 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; return 0;
} }
@@ -1045,7 +1046,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
/* Set system time only if the step is larger than 1 second */ /* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) { if (fabs(sys_offset) >= 1.0) {
if (LCL_ApplyStepOffset(sys_offset)) if (LCL_ApplyStepOffset(sys_offset))
LOG(LOGS_INFO, LOGF_RtcLinux, "System time set from RTC"); LOG(LOGS_INFO, "System time set from RTC");
} }
} else { } else {
return 0; return 0;
@@ -1090,7 +1091,7 @@ RTC_Linux_Trim(void)
if (fabs(coef_seconds_fast) > 1.0) { 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); coef_seconds_fast);
/* Do processing to set clock. Let R be the value we set the /* Do processing to set clock. Let R be the value we set the

16
sched.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -163,7 +163,7 @@ SCH_AddFileHandler
assert(fd >= 0); assert(fd >= 0);
if (fd >= FD_SETSIZE) 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 */ /* Resize the array if the descriptor is highest so far */
while (ARR_GetSize(file_handlers) <= fd) { while (ARR_GetSize(file_handlers) <= fd) {
@@ -350,7 +350,7 @@ SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArg
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
UTI_AddDoubleToTimespec(&now, delay, &then); UTI_AddDoubleToTimespec(&now, delay, &then);
if (UTI_CompareTimespecs(&now, &then) > 0) { if (UTI_CompareTimespecs(&now, &then) > 0) {
LOG_FATAL(LOGF_Scheduler, "Timeout overflow"); LOG_FATAL("Timeout overflow");
} }
return SCH_AddTimeout(&then, handler, arg); return SCH_AddTimeout(&then, handler, arg);
@@ -512,7 +512,7 @@ dispatch_timeouts(struct timespec *now) {
more time than was delay of a scheduled timeout. */ more time than was delay of a scheduled timeout. */
if (n_done > n_timer_queue_entries * 4 && if (n_done > n_timer_queue_entries * 4 &&
n_done > n_entries_on_start * 4) { n_done > n_entries_on_start * 4) {
LOG_FATAL(LOGF_Scheduler, "Possible infinite loop in scheduling"); LOG_FATAL("Possible infinite loop in scheduling");
} }
} }
} }
@@ -680,10 +680,10 @@ check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec > if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
raw->tv_sec + JUMP_DETECT_THRESHOLD) { 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 < } else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
raw->tv_sec) { raw->tv_sec) {
LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!"); LOG(LOGS_WARN, "Forward time jump detected!");
} else { } else {
return 1; return 1;
} }
@@ -742,7 +742,7 @@ SCH_MainLoop(void)
/* if there are no file descriptors being waited on and no /* if there are no file descriptors being waited on and no
timeout set, this is clearly ridiculous, so stop the run */ timeout set, this is clearly ridiculous, so stop the run */
if (!ptv && !p_read_fds && !p_write_fds) if (!ptv && !p_read_fds && !p_write_fds)
LOG_FATAL(LOGF_Scheduler, "Nothing to do"); LOG_FATAL("Nothing to do");
status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv); status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
errsv = errno; errsv = errno;
@@ -762,7 +762,7 @@ SCH_MainLoop(void)
if (status < 0) { if (status < 0) {
if (!need_to_exit && errsv != EINTR) { 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) { } else if (status > 0) {
/* A file descriptor is ready for input or output */ /* A file descriptor is ready for input or output */

View File

@@ -208,7 +208,7 @@ update_stages(void)
stages[2].length = l3; stages[2].length = l3;
for (i = 0; i < NUM_STAGES; i++) { 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); i + 1, stages[i].wander, stages[i].length);
} }
} }
@@ -230,7 +230,7 @@ update_smoothing(struct timespec *now, double offset, double freq)
update_stages(); 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 static void
@@ -295,7 +295,7 @@ SMT_Activate(struct timespec *now)
if (!enabled || !locked) if (!enabled || !locked)
return; 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)" : ""); " (leap seconds only)" : "");
locked = 0; locked = 0;
last_update = *now; last_update = *now;

View File

@@ -348,12 +348,12 @@ void SRC_AccumulateSample
inst->leap_status = leap_status; inst->leap_status = leap_status;
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d", DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimespecToString(sample_time), -offset, source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
root_delay, root_dispersion, stratum); root_delay, root_dispersion, stratum);
if (REF_IsLeapSecondClose()) { if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second"); LOG(LOGS_INFO, "Dropping sample around leap second");
return; return;
} }
@@ -425,10 +425,11 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
} }
/* Try to replace NTP sources that are unreachable, falsetickers, or /* 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 && if (inst->type == SRC_NTP &&
((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) || ((!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); NSR_HandleBadSource(inst->ip_addr);
} }
} }
@@ -450,7 +451,7 @@ log_selection_message(char *format, char *arg)
{ {
if (REF_GetMode() != REF_ModeNormal) if (REF_GetMode() != REF_ModeNormal)
return; return;
LOG(LOGS_INFO, LOGF_Sources, format, arg); LOG(LOGS_INFO, format, arg);
} }
/* ================================================== */ /* ================================================== */
@@ -562,7 +563,7 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
offset_weight = 1.0 / sources[index]->sel_info.root_distance; offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / src_skew; 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); index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
sum_offset_weight += offset_weight; sum_offset_weight += offset_weight;
@@ -583,7 +584,7 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
*frequency = sum_frequency / sum_frequency_weight; *frequency = sum_frequency / sum_frequency_weight;
*skew = 1.0 / sqrt(inv_sum2_skew); *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); *offset, *offset_sd, *frequency, *skew);
return combined; return combined;
@@ -735,12 +736,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
uint32_t local_ref_id = NSR_GetLocalRefid(sources[orphan_source]->ip_addr); uint32_t local_ref_id = NSR_GetLocalRefid(sources[orphan_source]->ip_addr);
if (!local_ref_id) { 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) { } else if (sources[orphan_source]->ref_id < local_ref_id) {
sources[orphan_source]->status = SRC_OK; sources[orphan_source]->status = SRC_OK;
n_sel_sources = 1; n_sel_sources = 1;
DEBUG_LOG(LOGF_Sources, "selecting orphan refid=%"PRIx32, DEBUG_LOG("selecting orphan refid=%"PRIx32, sources[orphan_source]->ref_id);
sources[orphan_source]->ref_id);
} }
} }
@@ -764,7 +764,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints += 2; 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, n_badstats_sources, n_sel_sources, max_badstat_reach,
max_sel_reach, max_reach_sample_ago); max_sel_reach, max_reach_sample_ago);
@@ -1002,7 +1002,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
sources[i]->sel_score = 1.0 / distance; 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, sources[i]->sel_score, sources[i]->ref_id,
updated_inst ? updated_inst->ref_id : 0, updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance); sources[i]->status, distance);
@@ -1026,7 +1026,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[max_score_index]->updates == 0) { if (sources[max_score_index]->updates == 0) {
selected_source_index = INVALID_SOURCE; selected_source_index = INVALID_SOURCE;
mark_ok_sources(SRC_WAITS_UPDATE); mark_ok_sources(SRC_WAITS_UPDATE);
DEBUG_LOG(LOGF_Sources, "best source has no updates"); DEBUG_LOG("best source has no updates");
return; return;
} }
@@ -1097,7 +1097,7 @@ SRC_SetReselectDistance(double distance)
{ {
if (reselect_distance != distance) { if (reselect_distance != distance) {
reselect_distance = distance; reselect_distance = distance;
LOG(LOGS_INFO, LOGF_Sources, "New reselect distance %f", distance); LOG(LOGS_INFO, "New reselect distance %f", distance);
} }
} }
@@ -1152,7 +1152,7 @@ FILE *open_dumpfile(SRC_Instance inst, const char *mode)
dumpdir = CNF_GetDumpDir(); dumpdir = CNF_GetDumpDir();
if (dumpdir[0] == '\0') { if (dumpdir[0] == '\0') {
LOG(LOGS_WARN, LOGF_Sources, "dumpdir not specified"); LOG(LOGS_WARN, "dumpdir not specified");
return NULL; return NULL;
} }
@@ -1163,13 +1163,13 @@ FILE *open_dumpfile(SRC_Instance inst, const char *mode)
(inst->type != SRC_NTP && (inst->type != SRC_NTP &&
snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat", snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat",
dumpdir, inst->ref_id) >= sizeof (filename))) { dumpdir, inst->ref_id) >= sizeof (filename))) {
LOG(LOGS_WARN, LOGF_Sources, "dumpdir too long"); LOG(LOGS_WARN, "dumpdir too long");
return NULL; return NULL;
} }
f = fopen(filename, mode); f = fopen(filename, mode);
if (!f && mode[0] != 'r') if (!f && mode[0] != 'r')
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file for %s", LOG(LOGS_WARN, "Could not open dump file for %s",
source_to_string(inst)); source_to_string(inst));
return f; return f;
@@ -1206,10 +1206,10 @@ SRC_ReloadSources(void)
if (!in) if (!in)
continue; continue;
if (!SST_LoadFromFile(sources[i]->stats, in)) if (!SST_LoadFromFile(sources[i]->stats, in))
LOG(LOGS_WARN, LOGF_Sources, "Could not load dump file for %s", LOG(LOGS_WARN, "Could not load dump file for %s",
source_to_string(sources[i])); source_to_string(sources[i]));
else else
LOG(LOGS_INFO, LOGF_Sources, "Loaded dump file for %s", LOG(LOGS_INFO, "Loaded dump file for %s",
source_to_string(sources[i])); source_to_string(sources[i]));
fclose(in); fclose(in);
} }
@@ -1247,7 +1247,7 @@ SRC_RemoveDumpFiles(void)
if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr)) if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
continue; continue;
DEBUG_LOG(LOGF_Sources, "Removing %s", gl.gl_pathv[i]); DEBUG_LOG("Removing %s", gl.gl_pathv[i]);
unlink(gl.gl_pathv[i]); unlink(gl.gl_pathv[i]);
} }

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -51,9 +51,6 @@
#define MIN_SKEW 1.0e-12 #define MIN_SKEW 1.0e-12
#define MAX_SKEW 1.0e+02 #define MAX_SKEW 1.0e+02
/* The minimum assumed std dev for weighting */
#define MIN_WEIGHT_SD 1.0e-9
/* The asymmetry of network jitter when all jitter is in one direction */ /* The asymmetry of network jitter when all jitter is in one direction */
#define MAX_ASYMMETRY 0.5 #define MAX_ASYMMETRY 0.5
@@ -295,7 +292,7 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
/* Make sure it's newer than the last sample */ /* Make sure it's newer than the last sample */
if (inst->n_samples && if (inst->n_samples &&
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) { UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s", LOG(LOGS_WARN, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid)); inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
SST_ResetInstance(inst); SST_ResetInstance(inst);
} }
@@ -466,7 +463,7 @@ correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
time. E.g. a value of 4 means that we think the standard deviation time. E.g. a value of 4 means that we think the standard deviation
is four times the fluctuation of the peer distance */ 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 /* This function runs the linear regression operation on the data. It
@@ -486,9 +483,10 @@ SST_DoNewRegression(SST_Stats inst)
int best_start, times_back_start; int best_start, times_back_start;
double est_intercept, est_slope, est_var, est_intercept_sd, est_slope_sd; double est_intercept, est_slope, est_var, est_intercept_sd, est_slope_sd;
int i, j, nruns; int i, j, nruns;
double min_distance, mean_distance; double min_distance, median_distance;
double sd_weight, sd; double sd_weight, sd;
double old_skew, old_freq, stress; double old_skew, old_freq, stress;
double precision;
convert_to_intervals(inst, times_back + inst->runs_samples); convert_to_intervals(inst, times_back + inst->runs_samples);
@@ -497,24 +495,28 @@ SST_DoNewRegression(SST_Stats inst)
offsets[i + inst->runs_samples] = inst->offsets[get_runsbuf_index(inst, i)]; 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); j = get_buf_index(inst, i);
peer_distances[i] = 0.5 * inst->peer_delays[get_runsbuf_index(inst, i)] + peer_distances[i] = 0.5 * inst->peer_delays[get_runsbuf_index(inst, i)] +
inst->peer_dispersions[j]; inst->peer_dispersions[j];
mean_distance += peer_distances[i];
if (peer_distances[i] < min_distance) { if (peer_distances[i] < min_distance) {
min_distance = peer_distances[i]; min_distance = peer_distances[i];
} }
} }
mean_distance /= inst->n_samples;
/* And now, work out the weight vector */ /* And now, work out the weight vector */
sd = mean_distance - min_distance; precision = LCL_GetSysPrecisionAsQuantum();
sd = CLAMP(MIN_WEIGHT_SD, sd, min_distance); 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++) { 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; weights[i] = sd_weight * sd_weight;
} }
} }
@@ -545,7 +547,7 @@ SST_DoNewRegression(SST_Stats inst)
inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW); inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
stress = fabs(old_freq - inst->estimated_frequency) / old_skew; stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
DEBUG_LOG(LOGF_SourceStats, "off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d", 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->estimated_offset, inst->estimated_frequency, inst->skew,
inst->n_samples, best_start, inst->nruns, inst->n_samples, best_start, inst->nruns,
inst->asymmetry, inst->asymmetry_run); inst->asymmetry, inst->asymmetry_run);
@@ -624,7 +626,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
*stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)]; *stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
*std_dev = inst->std_dev; *std_dev = inst->std_dev;
sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]); sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency; offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency;
*root_distance = 0.5 * inst->root_delays[j] + *root_distance = 0.5 * inst->root_delays[j] +
inst->root_dispersions[j] + sample_elapsed * inst->skew; inst->root_dispersions[j] + sample_elapsed * inst->skew;
@@ -653,7 +655,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
*select_ok = inst->regression_ok; *select_ok = inst->regression_ok;
DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d", 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, inst->n_samples, offset, *root_distance, *std_dev,
*first_sample_ago, *last_sample_ago, *select_ok); *first_sample_ago, *last_sample_ago, *select_ok);
} }
@@ -684,8 +686,9 @@ SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
elapsed_sample = UTI_DiffTimespecsToDouble(&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; *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", 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); inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew,
*average_offset, *offset_sd, *root_dispersion);
} }
@@ -719,7 +722,7 @@ SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doff
inst->estimated_offset += delta_time; inst->estimated_offset += delta_time;
inst->estimated_frequency = (inst->estimated_frequency - dfreq) / (1.0 - dfreq); 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, inst->n_samples, inst->runs_samples,
UTI_TimespecToString(&prev), UTI_TimespecToString(&inst->offset_time), UTI_TimespecToString(&prev), UTI_TimespecToString(&inst->offset_time),
prev_offset, inst->estimated_offset, prev_offset, inst->estimated_offset,
@@ -781,7 +784,7 @@ SST_IsGoodSample(SST_Stats inst, double offset, double delay,
{ {
double elapsed, allowed_increase, delay_increase; double elapsed, allowed_increase, delay_increase;
if (inst->n_samples < 3) if (inst->n_samples < 6)
return 1; return 1;
elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time); elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
@@ -805,8 +808,8 @@ SST_IsGoodSample(SST_Stats inst, double offset, double delay,
if (fabs(offset) - delay_increase > allowed_increase) if (fabs(offset) - delay_increase > allowed_increase)
return 1; return 1;
DEBUG_LOG(LOGF_SourceStats, "Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f", DEBUG_LOG("Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f",
offset, delay, allowed_increase, delay_increase); offset, delay, delay_increase, allowed_increase);
return 0; return 0;
} }
@@ -928,7 +931,7 @@ void
SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now) SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now)
{ {
int i, j; int i, j;
struct timespec ago; struct timespec last_sample_time;
if (inst->n_samples > 0) { if (inst->n_samples > 0) {
i = get_runsbuf_index(inst, inst->n_samples - 1); i = get_runsbuf_index(inst, inst->n_samples - 1);
@@ -938,8 +941,10 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j]; report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->strata[j]; report->stratum = inst->strata[j];
UTI_DiffTimespecs(&ago, now, &inst->sample_times[i]); /* Align the sample time to reduce the leak of the receive timestamp */
report->latest_meas_ago = ago.tv_sec; last_sample_time = inst->sample_times[i];
last_sample_time.tv_nsec = 0;
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else { } else {
report->latest_meas_ago = (uint32_t)-1; report->latest_meas_ago = (uint32_t)-1;
report->orig_latest_meas = 0; report->orig_latest_meas = 0;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -91,7 +91,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->arg = anything; inst->arg = anything;
if (pipe(inst->pipe)) if (pipe(inst->pipe))
LOG_FATAL(LOGF_Nameserv, "pipe() failed"); LOG_FATAL("pipe() failed");
UTI_FdSetCloexec(inst->pipe[0]); UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]); UTI_FdSetCloexec(inst->pipe[1]);

24
sys.c
View File

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

2
sys.h
View File

@@ -30,7 +30,7 @@
#define GOT_SYS_H #define GOT_SYS_H
/* Called at the start of the run to do initialisation */ /* 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 */ /* Called at the end of the run to do final clean-up */
extern void SYS_Finalise(void); extern void SYS_Finalise(void);

View File

@@ -129,7 +129,7 @@ start_fastslew(void)
drv_accrue_offset(offset_register, 0.0); 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; offset_register = 0.0;
fastslew_active = 1; fastslew_active = 1;
@@ -246,7 +246,7 @@ update_slew(void)
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL); slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
slew_start = now; 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, offset_register, correction_rate, base_freq, total_freq, slew_freq,
duration, slew_error); duration, slew_error);
} }
@@ -333,7 +333,7 @@ apply_step_offset(double offset)
UTI_TimespecToTimeval(&new_time, &new_time_tv); UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time_tv, NULL) < 0) { if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed"); DEBUG_LOG("settimeofday() failed");
return 0; return 0;
} }

View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009 * 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 * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -47,13 +47,14 @@
#include <sys/capability.h> #include <sys/capability.h>
#endif #endif
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_SCFILTER #ifdef FEAT_SCFILTER
#include <sys/prctl.h> #include <sys/prctl.h>
#include <seccomp.h> #include <seccomp.h>
#include <termios.h> #include <termios.h>
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_PPS #ifdef FEAT_PPS
#include <linux/pps.h> #include <linux/pps.h>
#endif #endif
@@ -68,6 +69,7 @@
#include "sys_linux.h" #include "sys_linux.h"
#include "sys_timex.h" #include "sys_timex.h"
#include "conf.h" #include "conf.h"
#include "local.h"
#include "logging.h" #include "logging.h"
#include "privops.h" #include "privops.h"
#include "util.h" #include "util.h"
@@ -238,7 +240,7 @@ guess_hz(void)
} }
/* oh dear. doomed. */ /* 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; return 0;
} }
@@ -281,11 +283,11 @@ get_kernel_version(int *major, int *minor, int *patch)
struct utsname uts; struct utsname uts;
if (uname(&uts) < 0) if (uname(&uts) < 0)
LOG_FATAL(LOGF_SysLinux, "uname() failed"); LOG_FATAL("uname() failed");
*patch = 0; *patch = 0;
if (sscanf(uts.release, "%d.%d.%d", major, minor, patch) < 2) if (sscanf(uts.release, "%d.%d.%d", major, minor, patch) < 2)
LOG_FATAL(LOGF_SysLinux, "Could not parse kernel version"); LOG_FATAL("Could not parse kernel version");
} }
/* ================================================== */ /* ================================================== */
@@ -312,10 +314,10 @@ get_version_specific_details(void)
tick_update_hz = 100; tick_update_hz = 100;
get_kernel_version(&major, &minor, &patch); get_kernel_version(&major, &minor, &patch);
DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", 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) { 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 && if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
@@ -332,7 +334,7 @@ get_version_specific_details(void)
have_setoffset = 1; 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); hz, nominal_tick, max_tick_bias);
} }
@@ -389,7 +391,7 @@ SYS_Linux_Initialise(void)
reset_adjtime_offset(); reset_adjtime_offset();
if (have_setoffset && !test_step_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; have_setoffset = 0;
} }
@@ -419,7 +421,7 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
cap_t cap; cap_t cap;
if (prctl(PR_SET_KEEPCAPS, 1)) { if (prctl(PR_SET_KEEPCAPS, 1)) {
LOG_FATAL(LOGF_SysLinux, "prctl() failed"); LOG_FATAL("prctl() failed");
} }
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
@@ -429,11 +431,11 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
"cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep"; "cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep";
if ((cap = cap_from_text(cap_text)) == NULL) { 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)) { if (cap_set_proc(cap)) {
LOG_FATAL(LOGF_SysLinux, "cap_set_proc() failed"); LOG_FATAL("cap_set_proc() failed");
} }
cap_free(cap); cap_free(cap);
@@ -452,7 +454,7 @@ void check_seccomp_applicability(void)
CNF_GetMailOnChange(&mail_enabled, &mail_threshold, &mail_user); CNF_GetMailOnChange(&mail_enabled, &mail_threshold, &mail_user);
if (mail_enabled) 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");
} }
/* ================================================== */ /* ================================================== */
@@ -465,9 +467,10 @@ SYS_Linux_EnableSystemCallFilter(int level)
SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday), SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday),
SCMP_SYS(settimeofday), SCMP_SYS(time), SCMP_SYS(settimeofday), SCMP_SYS(time),
/* Process */ /* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getrlimit), SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid),
SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigprocmask), SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn),
SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), SCMP_SYS(wait4), SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn),
SCMP_SYS(wait4),
/* Memory */ /* Memory */
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2), SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2),
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt), SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
@@ -487,7 +490,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select), SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
SCMP_SYS(set_robust_list), SCMP_SYS(write), SCMP_SYS(set_robust_list), SCMP_SYS(write),
/* Miscellaneous */ /* Miscellaneous */
SCMP_SYS(uname), SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname),
}; };
const int socket_domains[] = { const int socket_domains[] = {
@@ -514,7 +517,10 @@ SYS_Linux_EnableSystemCallFilter(int level)
const static unsigned long ioctls[] = { const static unsigned long ioctls[] = {
FIONREAD, TCGETS, FIONREAD, TCGETS,
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING) #if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_SYS_OFFSET, PTP_EXTTS_REQUEST, PTP_PIN_SETFUNC, PTP_SYS_OFFSET,
#ifdef PTP_SYS_OFFSET_PRECISE
PTP_SYS_OFFSET_PRECISE,
#endif
#endif #endif
#ifdef FEAT_PPS #ifdef FEAT_PPS
PPS_FETCH, PPS_FETCH,
@@ -541,7 +547,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP); ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
if (ctx == NULL) if (ctx == NULL)
LOG_FATAL(LOGF_SysLinux, "Failed to initialize seccomp"); LOG_FATAL("Failed to initialize seccomp");
/* Add system calls that are always allowed */ /* Add system calls that are always allowed */
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) { for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
@@ -582,14 +588,14 @@ SYS_Linux_EnableSystemCallFilter(int level)
} }
if (seccomp_load(ctx) < 0) 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); seccomp_release(ctx);
return; return;
add_failed: add_failed:
LOG_FATAL(LOGF_SysLinux, "Failed to add seccomp rules"); LOG_FATAL("Failed to add seccomp rules");
} }
#endif #endif
@@ -603,7 +609,7 @@ void SYS_Linux_SetScheduler(int SchedPriority)
struct sched_param sched; struct sched_param sched;
if (SchedPriority < 1 || SchedPriority > 99) { if (SchedPriority < 1 || SchedPriority > 99) {
LOG_FATAL(LOGF_SysLinux, "Bad scheduler priority: %d", SchedPriority); LOG_FATAL("Bad scheduler priority: %d", SchedPriority);
} else { } else {
sched.sched_priority = SchedPriority; sched.sched_priority = SchedPriority;
pmax = sched_get_priority_max(SCHED_FIFO); pmax = sched_get_priority_max(SCHED_FIFO);
@@ -615,10 +621,10 @@ void SYS_Linux_SetScheduler(int SchedPriority)
sched.sched_priority = pmin; sched.sched_priority = pmin;
} }
if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) { if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) {
LOG(LOGS_ERR, LOGF_SysLinux, "sched_setscheduler() failed"); LOG(LOGS_ERR, "sched_setscheduler() failed");
} }
else { else {
DEBUG_LOG(LOGF_SysLinux, "Enabled SCHED_FIFO with priority %d", DEBUG_LOG("Enabled SCHED_FIFO with priority %d",
sched.sched_priority); sched.sched_priority);
} }
} }
@@ -636,14 +642,14 @@ void SYS_Linux_MemLockAll(int LockAll)
rlim.rlim_max = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY;
rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) { 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 { else {
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) { if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
LOG(LOGS_ERR, LOGF_SysLinux, "mlockall() failed"); LOG(LOGS_ERR, "mlockall() failed");
} }
else { else {
DEBUG_LOG(LOGF_SysLinux, "Successfully locked into RAM"); DEBUG_LOG("Successfully locked into RAM");
} }
} }
} }
@@ -661,3 +667,205 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
return kernelvercmp(req_major, req_minor, 0, major, minor, patch) <= 0; 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

@@ -41,4 +41,14 @@ extern void SYS_Linux_SetScheduler(int SchedPriority);
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor); 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 */ #endif /* GOT_SYS_LINUX_H */

View File

@@ -46,6 +46,15 @@
#include "privops.h" #include "privops.h"
#include "util.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 /* This register contains the number of seconds by which the local
@@ -116,7 +125,7 @@ clock_initialise(void)
newadj.tv_usec = 0; newadj.tv_usec = 0;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) { if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); LOG_FATAL("adjtime() failed");
} }
} }
@@ -154,9 +163,8 @@ start_adjust(void)
predicted_error = (current_drift_removal_interval - drift_removal_elapsed) / 2.0 * current_freq; 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", DEBUG_LOG("drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
1.0e6 * drift_removal_elapsed, 1.0e6 * drift_removal_elapsed, 1.0e6 * current_drift_removal_interval,
1.0e6 * current_drift_removal_interval,
1.0e6 * predicted_error); 1.0e6 * predicted_error);
adjust_required = - (accrued_error + offset_register + predicted_error); adjust_required = - (accrued_error + offset_register + predicted_error);
@@ -166,7 +174,7 @@ start_adjust(void)
rounding_error = adjust_required - adjustment_requested; rounding_error = adjust_required - adjustment_requested;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) { if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); LOG_FATAL("adjtime() failed");
} }
old_adjust_remaining = UTI_TimevalToDouble(&oldadj); old_adjust_remaining = UTI_TimevalToDouble(&oldadj);
@@ -190,7 +198,7 @@ stop_adjust(void)
zeroadj.tv_usec = 0; zeroadj.tv_usec = 0;
if (PRV_AdjustTime(&zeroadj, &remadj) < 0) { if (PRV_AdjustTime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); LOG_FATAL("adjtime() failed");
} }
LCL_ReadRawTime(&T1); LCL_ReadRawTime(&T1);
@@ -239,7 +247,7 @@ apply_step_offset(double offset)
UTI_TimespecToTimeval(&new_time, &new_time_tv); UTI_TimespecToTimeval(&new_time, &new_time_tv);
if (PRV_SetTime(&new_time_tv, NULL) < 0) { if (PRV_SetTime(&new_time_tv, NULL) < 0) {
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed"); DEBUG_LOG("settimeofday() failed");
return 0; return 0;
} }
@@ -338,14 +346,14 @@ set_sync_status(int synchronised, double est_error, double max_error)
/* update the RTC by applying a step of 0.0 secs */ /* update the RTC by applying a step of 0.0 secs */
apply_step_offset(0.0); apply_step_offset(0.0);
last_rtc_sync = now; last_rtc_sync = now;
DEBUG_LOG(LOGF_SysMacOSX, "rtc synchronised"); DEBUG_LOG("rtc synchronised");
} }
} }
interval = ERROR_WEIGHT * est_error / (fabs(current_freq) + FREQUENCY_RES); interval = ERROR_WEIGHT * est_error / (fabs(current_freq) + FREQUENCY_RES);
drift_removal_interval = MAX(interval, DRIFT_REMOVAL_INTERVAL_MIN); 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); est_error * 1.0e6, current_freq * 1.0e6, interval, drift_removal_interval);
} }
@@ -389,7 +397,7 @@ set_realtime(void)
THREAD_TIME_CONSTRAINT_POLICY_COUNT); THREAD_TIME_CONSTRAINT_POLICY_COUNT);
if (kr != KERN_SUCCESS) { 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 -1;
} }
return 0; return 0;
@@ -418,8 +426,8 @@ void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid)
/* ================================================== */ /* ================================================== */
void static void
SYS_MacOSX_Initialise(void) legacy_MacOSX_Initialise(void)
{ {
clock_initialise(); clock_initialise();
@@ -435,8 +443,8 @@ SYS_MacOSX_Initialise(void)
/* ================================================== */ /* ================================================== */
void static void
SYS_MacOSX_Finalise(void) legacy_MacOSX_Finalise(void)
{ {
SCH_RemoveTimeout(drift_removal_id); SCH_RemoveTimeout(drift_removal_id);
@@ -445,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 #endif

View File

@@ -65,14 +65,14 @@ accrue_offset(double offset, double corr_rate)
UTI_DoubleToTimeval(-offset, &newadj); UTI_DoubleToTimeval(-offset, &newadj);
if (PRV_AdjustTime(&newadj, &oldadj) < 0) if (PRV_AdjustTime(&newadj, &oldadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); LOG_FATAL("adjtime() failed");
/* Add the old remaining adjustment if not zero */ /* Add the old remaining adjustment if not zero */
doldadj = UTI_TimevalToDouble(&oldadj); doldadj = UTI_TimevalToDouble(&oldadj);
if (doldadj != 0.0) { if (doldadj != 0.0) {
UTI_DoubleToTimeval(-offset + doldadj, &newadj); UTI_DoubleToTimeval(-offset + doldadj, &newadj);
if (PRV_AdjustTime(&newadj, NULL) < 0) if (PRV_AdjustTime(&newadj, NULL) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); LOG_FATAL("adjtime() failed");
} }
} }
@@ -86,7 +86,7 @@ get_offset_correction(struct timespec *raw,
double adjustment_remaining; double adjustment_remaining;
if (PRV_AdjustTime(NULL, &remadj) < 0) if (PRV_AdjustTime(NULL, &remadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); LOG_FATAL("adjtime() failed");
adjustment_remaining = UTI_TimevalToDouble(&remadj); adjustment_remaining = UTI_TimevalToDouble(&remadj);
@@ -138,7 +138,7 @@ SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
/* Check if we have write access to /dev/clockctl */ /* Check if we have write access to /dev/clockctl */
fd = open("/dev/clockctl", O_WRONLY); fd = open("/dev/clockctl", O_WRONLY);
if (fd < 0) if (fd < 0)
LOG_FATAL(LOGF_SysNetBSD, "Can't write to /dev/clockctl"); LOG_FATAL("Can't write to /dev/clockctl");
close(fd); close(fd);
#endif #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 #define MIN_TICK_RATE 100
/* Saved timex status */ /* 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 static void
set_leap(int leap) set_leap(int leap, int tai_offset)
{ {
struct timex txc; struct timex txc;
int applied; int applied, prev_status;
applied = 0;
if (!leap) {
txc.modes = 0; txc.modes = 0;
if (SYS_Timex_Adjust(&txc, 1) == TIME_WAIT) applied = SYS_Timex_Adjust(&txc, 0) == TIME_WAIT;
applied = 1;
}
status &= ~(STA_INS | STA_DEL); prev_status = sys_status;
sys_status &= ~(STA_INS | STA_DEL);
if (leap > 0) if (leap > 0)
status |= STA_INS; sys_status |= STA_INS;
else if (leap < 0) else if (leap < 0)
status |= STA_DEL; sys_status |= STA_DEL;
txc.modes = MOD_STATUS; 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); SYS_Timex_Adjust(&txc, 0);
LOG(LOGS_INFO, LOGF_SysTimex, "System clock status %s leap second", if (prev_status != sys_status) {
LOG(LOGS_INFO, "System clock status %s leap second",
leap ? (leap > 0 ? "set to insert" : "set to delete") : leap ? (leap > 0 ? "set to insert" : "set to delete") :
(applied ? "reset after" : "set to not insert/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 #endif
if (synchronised) if (synchronised)
status &= ~STA_UNSYNC; sys_status &= ~STA_UNSYNC;
else else
status |= STA_UNSYNC; sys_status |= STA_UNSYNC;
txc.modes = MOD_STATUS | MOD_ESTERROR | MOD_MAXERROR; txc.modes = MOD_STATUS | MOD_ESTERROR | MOD_MAXERROR;
txc.status = status; txc.status = sys_status;
txc.esterror = est_error * 1.0e6; txc.esterror = est_error * 1.0e6;
txc.maxerror = max_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; struct timex txc;
status = STA_UNSYNC; sys_status = STA_UNSYNC;
sys_tai_offset = 0;
/* Reset PLL offset */ /* Reset PLL offset */
txc.modes = MOD_OFFSET | MOD_STATUS; txc.modes = MOD_OFFSET | MOD_STATUS;
txc.status = STA_PLL | status; txc.status = STA_PLL | sys_status;
txc.offset = 0; txc.offset = 0;
SYS_Timex_Adjust(&txc, 0); SYS_Timex_Adjust(&txc, 0);
/* Turn PLL off */ /* Turn PLL off */
txc.modes = MOD_STATUS; txc.modes = MOD_STATUS;
txc.status = status; txc.status = sys_status;
SYS_Timex_Adjust(&txc, 0); SYS_Timex_Adjust(&txc, 0);
} }
@@ -238,11 +257,9 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
if (state < 0) { if (state < 0) {
if (!ignore_error) if (!ignore_error)
LOG_FATAL(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s", LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
txc->modes, strerror(errno));
else else
DEBUG_LOG(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s", DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
txc->modes, strerror(errno));
} }
return state; return state;

View File

@@ -59,7 +59,7 @@
#include <time.h> #include <time.h>
#include <unistd.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> #include <sys/timex.h>
#endif #endif
@@ -76,4 +76,8 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#ifdef HAVE_GETRANDOM
#include <sys/random.h>
#endif
#endif /* GOT_SYSINCL_H */ #endif /* GOT_SYSINCL_H */

View File

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

View File

@@ -20,8 +20,8 @@ RMS offset : 0\.000...... seconds
Frequency : (99|100)\.... ppm fast Frequency : (99|100)\.... ppm fast
Residual freq : [+-][0-9]\.... ppm Residual freq : [+-][0-9]\.... ppm
Skew : [0-9]\.... ppm Skew : [0-9]\.... ppm
Root delay : 0\.000... seconds Root delay : 0\.000...... seconds
Root dispersion : 0\.000... seconds Root dispersion : 0\.000...... seconds
Update interval : [0-9]+\.. seconds Update interval : [0-9]+\.. seconds
Leap status : Normal Leap status : Normal
210 Number of sources = 1 210 Number of sources = 1

View File

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

View File

@@ -10,7 +10,7 @@ wander=0.0
freq_offset="(sum 1e-10)" freq_offset="(sum 1e-10)"
time_rms_limit=2e-4 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 run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
@@ -18,7 +18,7 @@ check_source_selection || test_fail
check_packet_interval || test_fail check_packet_interval || test_fail
check_sync || 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 run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
check_packet_interval || test_fail check_packet_interval || test_fail

View File

@@ -25,13 +25,21 @@ time_offset=-10
client_server_options="minpoll 6 maxpoll 6" client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100" client_conf="corrtimeratio 100"
min_sync_time=8000 min_sync_time=8000
max_sync_time=8500 max_sync_time=9000
run_test || test_fail run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
check_source_selection || test_fail check_source_selection || test_fail
check_sync || 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 min_sync_time=$default_min_sync_time
max_sync_time=$default_max_sync_time max_sync_time=$default_max_sync_time
time_max_limit=11 time_max_limit=11

View File

@@ -1,24 +1,90 @@
#!/bin/bash #!/bin/bash
passed=() failed=() skipped=() print_help() {
echo "$1 [-a] [-i ITERATIONS] [-m MAXFAILS] [-s SEED] [TEST]..."
}
run_test() {
local result name=$1 seed=$2
CLKNETSIM_RANDOM_SEED=$seed ./$name
result=$?
if [ $result -ne 0 -a $result -ne 9 ]; then
if [ $abort_on_fail -ne 0 ]; then
echo 1>&2
echo Failed with random seed $seed 1>&2
exit 1
fi
failed_seeds=(${failed_seeds[@]} $seed)
fi
return $result
}
abort_on_fail=0
iterations=1
max_fails=0
random_seed=${CLKNETSIM_RANDOM_SEED:-$RANDOM}
while getopts ":ai:m:s:" opt; do
case $opt in
a) abort_on_fail=1;;
i) iterations=$OPTARG;;
m) max_fails=$OPTARG;;
s) random_seed=$OPTARG;;
*) print_help "$0"; exit 3;;
esac
done
shift $[$OPTIND - 1]
passed=() failed=() skipped=() failed_seeds=()
[ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_]) [ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_])
for test in "${tests[@]}"; do for test in "${tests[@]}"; do
echo "$test ($[${#passed[@]} + ${#failed[@]} + 1]/${#tests[@]})" if [ $iterations -gt 1 ]; then
./$test printf "%-30s" "$test"
fails=0
for i in $(seq 1 $iterations); do
run_test $test $[$random_seed + $i - 1] > /dev/null
case $? in case $? in
0) echo -n ".";;
9) break;;
*) echo -n "x"; fails=$[$fails + 1];;
esac
done
if [ $i -lt $iterations ]; then
printf "%${iterations}s" ""
echo " SKIP"
result=9
elif [ $fails -gt $max_fails ]; then
echo " FAIL"
result=1
else
echo " PASS"
result=0
fi
else
printf "%s " "$test"
run_test $test $random_seed
result=$?
echo
fi
case $result in
0) passed=(${passed[@]} $test);; 0) passed=(${passed[@]} $test);;
9) skipped=(${skipped[@]} $test);; 9) skipped=(${skipped[@]} $test);;
*) failed=(${failed[@]} $test);; *) failed=(${failed[@]} $test);;
esac esac
echo
done done
echo
echo "SUMMARY:" echo "SUMMARY:"
echo " TOTAL $[${#passed[@]} + ${#failed[@]} + ${#skipped[@]}]" echo " TOTAL $[${#passed[@]} + ${#failed[@]} + ${#skipped[@]}]"
echo " PASSED ${#passed[@]}" echo " PASSED ${#passed[@]}"
echo " FAILED ${#failed[@]} (${failed[@]})" echo " FAILED ${#failed[@]} (${failed[@]}) (${failed_seeds[@]})"
echo " SKIPPED ${#skipped[@]} (${skipped[@]})" echo " SKIPPED ${#skipped[@]} (${skipped[@]})"
[ ${#failed[@]} -eq 0 ] [ ${#failed[@]} -eq 0 ]

View File

@@ -42,7 +42,7 @@ test_unit(void)
TST_GetRandomAddress(&ip, IPADDR_INET6, -1); TST_GetRandomAddress(&ip, IPADDR_INET6, -1);
} }
DEBUG_LOG(0, "address %s", UTI_IPToString(&ip)); DEBUG_LOG("address %s", UTI_IPToString(&ip));
sub = random() % (maxsub + 1); sub = random() % (maxsub + 1);

View File

@@ -33,7 +33,7 @@ test_unit(void)
"cmdratelimit interval 3 burst 4 leak 3", "cmdratelimit interval 3 burst 4 leak 3",
}; };
CNF_Initialise(0); CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++) for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]); CNF_ParseLine(NULL, i + 1, conf[i]);
@@ -42,14 +42,14 @@ test_unit(void)
TEST_CHECK(ARR_GetSize(records) == 16); TEST_CHECK(ARR_GetSize(records) == 16);
for (i = 0; i < 500; i++) { for (i = 0; i < 500; i++) {
DEBUG_LOG(0, "iteration %d", i); DEBUG_LOG("iteration %d", i);
ts.tv_sec = (time_t)random() & 0x0fffffff; ts.tv_sec = (time_t)random() & 0x0fffffff;
ts.tv_nsec = 0; ts.tv_nsec = 0;
for (j = 0; j < 1000; j++) { for (j = 0; j < 1000; j++) {
TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9); TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
DEBUG_LOG(0, "address %s", UTI_IPToString(&ip)); DEBUG_LOG("address %s", UTI_IPToString(&ip));
if (random() % 2) { if (random() % 2) {
index = CLG_LogNTPAccess(&ip, &ts); index = CLG_LogNTPAccess(&ip, &ts);
@@ -65,7 +65,7 @@ test_unit(void)
} }
} }
DEBUG_LOG(0, "records %d", ARR_GetSize(records)); DEBUG_LOG("records %d", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 64); TEST_CHECK(ARR_GetSize(records) == 64);
for (i = j = 0; i < 10000; i++) { for (i = j = 0; i < 10000; i++) {
@@ -76,7 +76,7 @@ test_unit(void)
j++; j++;
} }
DEBUG_LOG(0, "requests %u responses %u", i, j); DEBUG_LOG("requests %u responses %u", i, j);
TEST_CHECK(j * 4 < i && j * 6 > i); TEST_CHECK(j * 4 < i && j * 6 > i);
CLG_Finalise(); CLG_Finalise();

View File

@@ -31,7 +31,7 @@ test_unit(void)
LCL_Initialise(); LCL_Initialise();
clock = HCL_CreateInstance(); clock = HCL_CreateInstance(1.0);
for (i = 0; i < 2000; i++) { for (i = 0; i < 2000; i++) {
UTI_ZeroTimespec(&start_hw_ts); UTI_ZeroTimespec(&start_hw_ts);
@@ -39,11 +39,11 @@ test_unit(void)
UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts); UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts);
UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts); UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts);
DEBUG_LOG(0, "iteration %d", i); DEBUG_LOG("iteration %d", i);
freq = TST_GetRandomDouble(0.9, 1.1); freq = TST_GetRandomDouble(0.9, 1.1);
jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9); jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
interval = TST_GetRandomDouble(MIN_SAMPLE_SEPARATION / 10, MIN_SAMPLE_SEPARATION * 10.0); interval = TST_GetRandomDouble(0.1, 10.0);
clock->n_samples = 0; clock->n_samples = 0;
clock->valid_coefs = 0; clock->valid_coefs = 0;
@@ -68,5 +68,7 @@ test_unit(void)
} }
} }
HCL_DestroyInstance(clock);
LCL_Finalise(); LCL_Finalise();
} }

147
test/unit/keys.c Normal file
View File

@@ -0,0 +1,147 @@
/*
**********************************************************************
* 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.
*
**********************************************************************
*/
#include <keys.c>
#include "test.h"
#define KEYS 100
#define KEYFILE "keys.test-keys"
static
uint32_t write_random_key(FILE *f)
{
const char *hash_name;
char key[128];
uint32_t id;
int i, length;
length = random() % sizeof (key) + 1;
length = MAX(length, 4);
UTI_GetRandomBytes(&id, sizeof (id));
UTI_GetRandomBytes(key, length);
switch (random() % 6) {
#ifdef FEAT_SECHASH
case 0:
hash_name = "SHA1";
break;
case 1:
hash_name = "SHA256";
break;
case 2:
hash_name = "SHA384";
break;
case 3:
hash_name = "SHA512";
break;
#endif
case 4:
hash_name = "MD5";
break;
default:
hash_name = "";
}
fprintf(f, "%u %s %s", id, hash_name, random() % 2 ? "HEX:" : "");
for (i = 0; i < length; i++)
fprintf(f, "%02hhX", key[i]);
fprintf(f, "\n");
return id;
}
static void
generate_key_file(const char *name, uint32_t *keys)
{
FILE *f;
int i;
f = fopen(name, "w");
TEST_CHECK(f);
for (i = 0; i < KEYS; i++)
keys[i] = write_random_key(f);
fclose(f);
}
void
test_unit(void)
{
int i, j, data_len, auth_len;
uint32_t keys[KEYS], key;
unsigned char data[100], auth[MAX_HASH_LENGTH];
char conf[][100] = {
"keyfile "KEYFILE
};
CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
generate_key_file(KEYFILE, keys);
KEY_Initialise();
for (i = 0; i < 100; i++) {
DEBUG_LOG("iteration %d", i);
if (i) {
generate_key_file(KEYFILE, keys);
KEY_Reload();
}
UTI_GetRandomBytes(data, sizeof (data));
for (j = 0; j < KEYS; j++) {
TEST_CHECK(KEY_KeyKnown(keys[j]));
TEST_CHECK(KEY_GetAuthDelay(keys[j]) >= 0);
TEST_CHECK(KEY_GetAuthLength(keys[j]) >= 16);
data_len = random() % (sizeof (data) + 1);
auth_len = KEY_GenerateAuth(keys[j], data, data_len, auth, sizeof (auth));
TEST_CHECK(auth_len >= 16);
TEST_CHECK(KEY_CheckAuth(keys[j], data, data_len, auth, auth_len, auth_len));
if (j > 0 && keys[j - 1] != keys[j])
TEST_CHECK(!KEY_CheckAuth(keys[j - 1], data, data_len, auth, auth_len, auth_len));
auth_len = random() % auth_len + 1;
if (auth_len < MAX_HASH_LENGTH)
auth[auth_len]++;
TEST_CHECK(KEY_CheckAuth(keys[j], data, data_len, auth, auth_len, auth_len));
auth[auth_len - 1]++;
TEST_CHECK(!KEY_CheckAuth(keys[j], data, data_len, auth, auth_len, auth_len));
}
for (j = 0; j < 1000; j++) {
UTI_GetRandomBytes(&key, sizeof (key));
if (KEY_KeyKnown(key))
continue;
TEST_CHECK(!KEY_GenerateAuth(j, data, data_len, auth, sizeof (auth)));
TEST_CHECK(!KEY_CheckAuth(j, data, data_len, auth, auth_len, auth_len));
}
}
unlink(KEYFILE);
KEY_Finalise();
CNF_Finalise();
HSH_Finalise();
}

299
test/unit/ntp_core.c Normal file
View File

@@ -0,0 +1,299 @@
/*
**********************************************************************
* 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.
*
**********************************************************************
*/
#include <config.h>
#include <sysincl.h>
#include <cmdparse.h>
#include <conf.h>
#include <keys.h>
#include <ntp_io.h>
#include <sched.h>
#include <local.h>
#include "test.h"
static struct timespec current_time;
static NTP_Receive_Buffer req_buffer, res_buffer;
static int req_length, res_length;
#define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0)
#define NIO_CloseServerSocket(fd) assert(fd == 100)
#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
#define NIO_CloseClientSocket(fd) assert(fd == 101)
#define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1)
#define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1))
#define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \
add_timeout_in_class(delay, separation, randomness, class, handler, arg)
#define SCH_RemoveTimeout(id) assert(!id || id == 102)
#define LCL_ReadRawTime(ts) (*ts = current_time)
#define LCL_ReadCookedTime(ts, err) do {double *p = err; *ts = current_time; if (p) *p = 0.0;} while (0)
#define SRC_UpdateReachability(inst, reach)
#define SRC_ResetReachability(inst)
static SCH_TimeoutID
add_timeout_in_class(double min_delay, double separation, double randomness,
SCH_TimeoutClass class, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{
return 102;
}
#include <ntp_core.c>
static NCR_Instance inst;
static void
advance_time(double x)
{
UTI_AddDoubleToTimespec(&current_time, x, &current_time);
}
static void
send_request(void)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
uint32_t prev_tx_count;
prev_tx_count = inst->report.total_tx_count;
transmit_timeout(inst);
TEST_CHECK(!inst->valid_rx);
TEST_CHECK(!inst->updated_timestamps);
TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count);
advance_time(1e-4);
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_DAEMON;
NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length);
}
static void
send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth)
{
NTP_Packet *req, *res;
req = &req_buffer.ntp_pkt;
res = &res_buffer.ntp_pkt;
TEST_CHECK(req_length >= NTP_NORMAL_PACKET_LENGTH);
res->lvm = NTP_LVM(LEAP_Normal, NTP_LVM_TO_VERSION(req->lvm),
NTP_LVM_TO_MODE(req->lvm) == MODE_CLIENT ? MODE_SERVER : MODE_ACTIVE);
res->stratum = 1;
res->poll = req->poll;
res->precision = -20;
res->root_delay = UTI_DoubleToNtp32(0.1);
res->root_dispersion = UTI_DoubleToNtp32(0.1);
res->reference_id = 0;
UTI_ZeroNtp64(&res->reference_ts);
res->originate_ts = interleaved ? req->receive_ts : req->transmit_ts;
advance_time(TST_GetRandomDouble(1e-4, 1e-2));
UTI_TimespecToNtp64(&current_time, &res->receive_ts, NULL);
advance_time(TST_GetRandomDouble(-1e-4, 1e-3));
UTI_TimespecToNtp64(&current_time, &res->transmit_ts, NULL);
advance_time(TST_GetRandomDouble(1e-4, 1e-2));
if (!valid_ts) {
switch (random() % (allow_update ? 4 : 5)) {
case 0:
res->originate_ts.hi = random();
break;
case 1:
res->originate_ts.lo = random();
break;
case 2:
UTI_ZeroNtp64(&res->originate_ts);
break;
case 3:
UTI_ZeroNtp64(&res->receive_ts);
break;
case 4:
UTI_ZeroNtp64(&res->transmit_ts);
break;
default:
assert(0);
}
}
if (authenticated) {
res->auth_keyid = req->auth_keyid;
KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, NTP_NORMAL_PACKET_LENGTH,
res->auth_data, 16);
res_length = NTP_NORMAL_PACKET_LENGTH + 4 + 16;
} else {
res_length = NTP_NORMAL_PACKET_LENGTH;
}
if (!valid_auth) {
switch (random() % 3) {
case 0:
res->auth_keyid++;
break;
case 1:
res->auth_data[random() % 16]++;
break;
case 2:
res_length = NTP_NORMAL_PACKET_LENGTH;
break;
default:
assert(0);
}
}
}
static void
process_response(int valid, int updated)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
NTP_Packet *res;
uint32_t prev_rx_count, prev_valid_count;
struct timespec prev_rx_ts;
int prev_open_socket;
res = &res_buffer.ntp_pkt;
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) == MODE_ACTIVE ? 100 : 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_DAEMON;
prev_rx_count = inst->report.total_rx_count;
prev_valid_count = inst->report.total_valid_count;
prev_rx_ts = inst->local_rx.ts;
prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD;
NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
if (prev_open_socket)
TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count);
else
TEST_CHECK(prev_rx_count == inst->report.total_rx_count);
if (valid)
TEST_CHECK(prev_valid_count + 1 == inst->report.total_valid_count);
else
TEST_CHECK(prev_valid_count == inst->report.total_valid_count);
if (updated)
TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
else
TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
}
void
test_unit(void)
{
char source_line[] = "127.0.0.1";
char conf[][100] = {
"port 0",
"keyfile ntp_core.keys"
};
int i, j, interleaved, authenticated, valid, updated, has_updated;
CPS_NTP_Source source;
NTP_Remote_Address remote_addr;
CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
LCL_Initialise();
TST_RegisterDummyDrivers();
SCH_Initialise();
SRC_Initialise();
NIO_Initialise(IPADDR_UNSPEC);
NCR_Initialise();
REF_Initialise();
KEY_Initialise();
for (i = 0; i < 1000; i++) {
CPS_ParseNTPSourceAdd(source_line, &source);
if (random() % 2)
source.params.interleaved = 1;
if (random() % 2)
source.params.authkey = 1;
UTI_ZeroTimespec(&current_time);
advance_time(TST_GetRandomDouble(1.0, 1e9));
TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
remote_addr.port = 123;
inst = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
NCR_StartInstance(inst);
has_updated = 0;
for (j = 0; j < 50; j++) {
DEBUG_LOG("iteration %d, %d", i, j);
interleaved = random() % 2;
authenticated = random() % 2;
valid = (!interleaved || (source.params.interleaved && has_updated)) &&
(!source.params.authkey || authenticated);
updated = (valid || inst->mode == MODE_ACTIVE) &&
(!source.params.authkey || authenticated);
has_updated = has_updated || updated;
send_request();
send_response(interleaved, authenticated, 1, 0, 1);
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0);
process_response(0, 0);
}
send_response(interleaved, authenticated, 1, 1, 1);
process_response(valid, updated);
process_response(0, 0);
advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1);
process_response(0, 0);
advance_time(1.0);
send_response(interleaved, authenticated, 1, 1, 1);
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
}
NCR_DestroyInstance(inst);
}
KEY_Finalise();
REF_Finalise();
NCR_Finalise();
NIO_Finalise();
SRC_Finalise();
SCH_Finalise();
LCL_Finalise();
CNF_Finalise();
HSH_Finalise();
}

2
test/unit/ntp_core.keys Normal file
View File

@@ -0,0 +1,2 @@
1 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
2 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579

View File

@@ -34,7 +34,7 @@ test_unit(void)
memset(&params, 0, sizeof (params)); memset(&params, 0, sizeof (params));
CNF_Initialise(0); CNF_Initialise(0, 0);
CNF_ParseLine(NULL, 1, conf); CNF_ParseLine(NULL, 1, conf);
LCL_Initialise(); LCL_Initialise();
@@ -47,7 +47,7 @@ test_unit(void)
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
TEST_CHECK(ARR_GetSize(records) == 1); TEST_CHECK(ARR_GetSize(records) == 1);
DEBUG_LOG(0, "collision mod %u", 1U << i); DEBUG_LOG("collision mod %u", 1U << i);
for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) { for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
do { do {
@@ -59,7 +59,7 @@ test_unit(void)
if (!j) if (!j)
hash = UTI_IPToHash(&addrs[j].ip_addr); hash = UTI_IPToHash(&addrs[j].ip_addr);
DEBUG_LOG(0, "adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr), DEBUG_LOG("adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr),
UTI_IPToHash(&addrs[j].ip_addr) % (1U << i)); UTI_IPToHash(&addrs[j].ip_addr) % (1U << i));
NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, &params); NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, &params);
@@ -79,7 +79,7 @@ test_unit(void)
} }
for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) { for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
DEBUG_LOG(0, "removing source %s", UTI_IPToString(&addrs[j].ip_addr)); DEBUG_LOG("removing source %s", UTI_IPToString(&addrs[j].ip_addr));
NSR_RemoveSource(&addrs[j]); NSR_RemoveSource(&addrs[j]);
for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) { for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) {
@@ -96,4 +96,5 @@ test_unit(void)
SCH_Finalise(); SCH_Finalise();
LCL_Finalise(); LCL_Finalise();
CNF_Finalise(); CNF_Finalise();
HSH_Finalise();
} }

119
test/unit/regress.c Normal file
View File

@@ -0,0 +1,119 @@
/*
**********************************************************************
* 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.
*
**********************************************************************
*/
#include <regress.c>
#include "test.h"
#define POINTS 64
void
test_unit(void)
{
double x[POINTS], x2[POINTS], y[POINTS], w[POINTS];
double b0, b1, b2, s2, sb0, sb1, slope, slope2, intercept, sd, median;
double xrange, yrange, wrange, x2range;
int i, j, n, m, c1, c2, c3, runs, best_start, dof;
for (n = 3; n <= POINTS; n++) {
for (i = 0; i < 200; i++) {
slope = TST_GetRandomDouble(-0.1, 0.1);
intercept = TST_GetRandomDouble(-1.0, 1.0);
sd = TST_GetRandomDouble(1e-6, 1e-4);
slope2 = (random() % 2 ? 1 : -1) * TST_GetRandomDouble(0.1, 0.5);
DEBUG_LOG("iteration %d n=%d intercept=%e slope=%e sd=%e",
i, n, intercept, slope, sd);
for (j = 0; j < n; j++) {
x[j] = -j;
y[j] = intercept + slope * x[j] + (j % 2 ? 1 : -1) * TST_GetRandomDouble(1e-6, sd);
w[j] = TST_GetRandomDouble(1.0, 2.0);
x2[j] = (y[j] - intercept - slope * x[j]) / slope2;
}
RGR_WeightedRegression(x, y, w, n, &b0, &b1, &s2, &sb0, &sb1);
DEBUG_LOG("WR b0=%e b1=%e s2=%e sb0=%e sb1=%e", b0, b1, s2, sb0, sb1);
TEST_CHECK(fabs(b0 - intercept) < sd + 1e-3);
TEST_CHECK(fabs(b1 - slope) < sd);
if (RGR_FindBestRegression(x, y, w, n, 0, 3, &b0, &b1, &s2, &sb0, &sb1,
&best_start, &runs, &dof)) {
DEBUG_LOG("BR b0=%e b1=%e s2=%e sb0=%e sb1=%e runs=%d bs=%d dof=%d",
b0, b1, s2, sb0, sb1, runs, best_start, dof);
TEST_CHECK(fabs(b0 - intercept) < sd + 1e-3);
TEST_CHECK(fabs(b1 - slope) < sd);
}
if (RGR_MultipleRegress(x, x2, y, n, &b2)) {
DEBUG_LOG("MR b2=%e", b2);
TEST_CHECK(fabs(b2 - slope2) < 1e-6);
}
for (j = 0; j < n / 7; j++)
y[random() % n] += 100 * sd;
if (RGR_FindBestRobustRegression(x, y, n, 1e-8, &b0, &b1, &runs, &best_start)) {
DEBUG_LOG("BRR b0=%e b1=%e runs=%d bs=%d", b0, b1, runs, best_start);
TEST_CHECK(fabs(b0 - intercept) < sd + 1e-2);
TEST_CHECK(fabs(b1 - slope) < 5.0 * sd);
}
for (j = 0; j < n; j++)
x[j] = random() % 4 * TST_GetRandomDouble(-1000, 1000);
median = RGR_FindMedian(x, n);
for (j = c1 = c2 = c3 = 0; j < n; j++) {
if (x[j] < median)
c1++;
if (x[j] > median)
c3++;
else
c2++;
}
TEST_CHECK(c1 + c2 >= c3 && c1 <= c2 + c3);
xrange = TST_GetRandomDouble(1e-6, pow(10.0, random() % 10));
yrange = random() % 3 * TST_GetRandomDouble(0.0, pow(10.0, random() % 10));
wrange = random() % 3 * TST_GetRandomDouble(0.0, pow(10.0, random() % 10));
x2range = random() % 3 * TST_GetRandomDouble(0.0, pow(10.0, random() % 10));
m = random() % n;
for (j = 0; j < n; j++) {
x[j] = (j ? x[j - 1] : 0.0) + TST_GetRandomDouble(1e-6, xrange);
y[j] = TST_GetRandomDouble(-yrange, yrange);
w[j] = 1.0 + TST_GetRandomDouble(0.0, wrange);
x2[j] = TST_GetRandomDouble(-x2range, x2range);
}
RGR_WeightedRegression(x, y, w, n, &b0, &b1, &s2, &sb0, &sb1);
if (RGR_FindBestRegression(x + m, y + m, w, n - m, m, 3, &b0, &b1, &s2, &sb0, &sb1,
&best_start, &runs, &dof))
;
if (RGR_MultipleRegress(x, x2, y, n, &b2))
;
if (RGR_FindBestRobustRegression(x, y, n, 1e-8, &b0, &b1, &runs, &best_start))
;
}
}
}

View File

@@ -29,7 +29,7 @@ test_unit(void)
double offset, freq, wander; double offset, freq, wander;
char conf[] = "smoothtime 300 0.01"; char conf[] = "smoothtime 300 0.01";
CNF_Initialise(0); CNF_Initialise(0, 0);
CNF_ParseLine(NULL, 1, conf); CNF_ParseLine(NULL, 1, conf);
LCL_Initialise(); LCL_Initialise();
@@ -40,7 +40,7 @@ test_unit(void)
UTI_ZeroTimespec(&ts); UTI_ZeroTimespec(&ts);
SMT_Reset(&ts); SMT_Reset(&ts);
DEBUG_LOG(0, "iteration %d", i); DEBUG_LOG("iteration %d", i);
offset = (random() % 1000000 - 500000) / 1.0e6; offset = (random() % 1000000 - 500000) / 1.0e6;
freq = (random() % 1000000 - 500000) / 1.0e9; freq = (random() % 1000000 - 500000) / 1.0e9;

View File

@@ -31,7 +31,7 @@ test_unit(void)
double offset, delay, disp; double offset, delay, disp;
struct timespec ts; struct timespec ts;
CNF_Initialise(0); CNF_Initialise(0, 0);
LCL_Initialise(); LCL_Initialise();
TST_RegisterDummyDrivers(); TST_RegisterDummyDrivers();
SCH_Initialise(); SCH_Initialise();
@@ -41,7 +41,7 @@ test_unit(void)
REF_SetMode(REF_ModeIgnore); REF_SetMode(REF_ModeIgnore);
for (i = 0; i < 1000; i++) { for (i = 0; i < 1000; i++) {
DEBUG_LOG(0, "iteration %d", i); DEBUG_LOG("iteration %d", i);
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) { for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
TEST_CHECK(n_sources == j); TEST_CHECK(n_sources == j);
@@ -51,7 +51,7 @@ test_unit(void)
sel_options = i & random() & (SRC_SELECT_NOSELECT | SRC_SELECT_PREFER | sel_options = i & random() & (SRC_SELECT_NOSELECT | SRC_SELECT_PREFER |
SRC_SELECT_TRUST | SRC_SELECT_REQUIRE); SRC_SELECT_TRUST | SRC_SELECT_REQUIRE);
DEBUG_LOG(0, "added source %d options %d", j, sel_options); DEBUG_LOG("added source %d options %d", j, sel_options);
srcs[j] = SRC_CreateNewInstance(UTI_IPToRefid(&addr), SRC_NTP, sel_options, &addr, srcs[j] = SRC_CreateNewInstance(UTI_IPToRefid(&addr), SRC_NTP, sel_options, &addr,
SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES); SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES);
SRC_UpdateReachability(srcs[j], 1); SRC_UpdateReachability(srcs[j], 1);
@@ -68,7 +68,7 @@ test_unit(void)
delay = TST_GetRandomDouble(1.0e-6, 1.0e-1); delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
disp = TST_GetRandomDouble(1.0e-6, 1.0e-1); disp = TST_GetRandomDouble(1.0e-6, 1.0e-1);
DEBUG_LOG(0, "source %d sample %d offset %f delay %f disp %f", j, k, DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k,
offset, delay, disp); offset, delay, disp);
SRC_AccumulateSample(srcs[j], &ts, offset, delay, disp, delay, disp, SRC_AccumulateSample(srcs[j], &ts, offset, delay, disp, delay, disp,
@@ -81,7 +81,7 @@ test_unit(void)
double passed_lo = DBL_MAX, passed_hi = DBL_MIN; double passed_lo = DBL_MAX, passed_hi = DBL_MIN;
SRC_SelectSource(srcs[k]); SRC_SelectSource(srcs[k]);
DEBUG_LOG(0, "source %d status %d", k, sources[k]->status); DEBUG_LOG("source %d status %d", k, sources[k]->status);
for (l = 0; l <= j; l++) { for (l = 0; l <= j; l++) {
TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED); TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED);
@@ -114,7 +114,7 @@ test_unit(void)
} }
} }
DEBUG_LOG(0, "sources %d passed %d trusted %d/%d required %d/%d", j, passed, DEBUG_LOG("sources %d passed %d trusted %d/%d required %d/%d", j, passed,
trusted_passed, trusted, required_passed, required); trusted_passed, trusted, required_passed, required);
TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi)); TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi));
@@ -134,4 +134,5 @@ test_unit(void)
SCH_Finalise(); SCH_Finalise();
LCL_Finalise(); LCL_Finalise();
CNF_Finalise(); CNF_Finalise();
HSH_Finalise();
} }

View File

@@ -64,8 +64,12 @@ main(int argc, char **argv)
printf("Testing %-30s ", test_name); printf("Testing %-30s ", test_name);
fflush(stdout); fflush(stdout);
LOG_Initialise();
test_unit(); test_unit();
LOG_Finalise();
printf("PASS\n"); printf("PASS\n");
return 0; return 0;

View File

@@ -4,10 +4,11 @@
void test_unit(void) { void test_unit(void) {
NTP_int64 ntp_ts, ntp_fuzz; NTP_int64 ntp_ts, ntp_fuzz;
struct timespec ts, ts2; struct timespec ts, ts2;
struct sockaddr_un sun;
double x, y; double x, y;
Float f; Float f;
int i, j, c; int i, j, c;
char buf[16]; char buf[16], *s;
for (i = -31; i < 31; i++) { for (i = -31; i < 31; i++) {
x = pow(2.0, i); x = pow(2.0, i);
@@ -42,6 +43,10 @@ void test_unit(void) {
ntp_ts.hi = htonl(JAN_1970); ntp_ts.hi = htonl(JAN_1970);
ntp_ts.lo = 0xffffffff; ntp_ts.lo = 0xffffffff;
UTI_Ntp64ToTimespec(&ntp_ts, &ts); UTI_Ntp64ToTimespec(&ntp_ts, &ts);
TEST_CHECK(ts.tv_sec == 0);
TEST_CHECK(ts.tv_nsec == 999999999);
UTI_AddDoubleToTimespec(&ts, 1e-9, &ts);
TEST_CHECK(ts.tv_sec == 1); TEST_CHECK(ts.tv_sec == 1);
TEST_CHECK(ts.tv_nsec == 0); TEST_CHECK(ts.tv_nsec == 0);
@@ -74,6 +79,14 @@ void test_unit(void) {
TEST_CHECK(!UTI_IsZeroTimespec(&ts)); TEST_CHECK(!UTI_IsZeroTimespec(&ts));
TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts)); TEST_CHECK(!UTI_IsZeroNtp64(&ntp_ts));
ntp_ts.hi = 0;
ntp_ts.lo = 0;
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
TEST_CHECK(UTI_IsZeroTimespec(&ts));
UTI_TimespecToNtp64(&ts, &ntp_ts, NULL);
TEST_CHECK(UTI_IsZeroNtp64(&ntp_ts));
ntp_fuzz.hi = htonl(1); ntp_fuzz.hi = htonl(1);
ntp_fuzz.lo = htonl(3); ntp_fuzz.lo = htonl(3);
ntp_ts.hi = htonl(1); ntp_ts.hi = htonl(1);
@@ -137,4 +150,18 @@ void test_unit(void) {
c++; c++;
} }
TEST_CHECK(c > 46000 && c < 48000); TEST_CHECK(c > 46000 && c < 48000);
for (i = 1; i < 2 * BUFFER_LENGTH; i++) {
sun.sun_family = AF_UNIX;
for (j = 0; j + 1 < i && j + 1 < sizeof (sun.sun_path); j++)
sun.sun_path[j] = 'A' + j % 26;
sun.sun_path[j] = '\0';
s = UTI_SockaddrToString((struct sockaddr *)&sun);
if (i <= BUFFER_LENGTH) {
TEST_CHECK(!strcmp(s, sun.sun_path));
} else {
TEST_CHECK(!strncmp(s, sun.sun_path, BUFFER_LENGTH - 2));
TEST_CHECK(s[BUFFER_LENGTH - 2] == '>');
}
}
} }

94
util.c
View File

@@ -180,7 +180,7 @@ UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *
double double
UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b) UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b)
{ {
return (a->tv_sec - b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec); return ((double)a->tv_sec - (double)b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
} }
/* ================================================== */ /* ================================================== */
@@ -329,12 +329,14 @@ UTI_StringToIP(const char *addr, IPAddr *ip)
if (inet_pton(AF_INET, addr, &in4) > 0) { if (inet_pton(AF_INET, addr, &in4) > 0) {
ip->family = IPADDR_INET4; ip->family = IPADDR_INET4;
ip->_pad = 0;
ip->addr.in4 = ntohl(in4.s_addr); ip->addr.in4 = ntohl(in4.s_addr);
return 1; return 1;
} }
if (inet_pton(AF_INET6, addr, &in6) > 0) { if (inet_pton(AF_INET6, addr, &in6) > 0) {
ip->family = IPADDR_INET6; ip->family = IPADDR_INET6;
ip->_pad = 0;
memcpy(ip->addr.in6, in6.s6_addr, sizeof (ip->addr.in6)); memcpy(ip->addr.in6, in6.s6_addr, sizeof (ip->addr.in6));
return 1; return 1;
} }
@@ -344,6 +346,7 @@ UTI_StringToIP(const char *addr, IPAddr *ip)
n = sscanf(addr, "%lu.%lu.%lu.%lu", &a, &b, &c, &d); n = sscanf(addr, "%lu.%lu.%lu.%lu", &a, &b, &c, &d);
if (n == 4) { if (n == 4) {
ip->family = IPADDR_INET4; ip->family = IPADDR_INET4;
ip->_pad = 0;
ip->addr.in4 = ((a & 0xff) << 24) | ((b & 0xff) << 16) | ip->addr.in4 = ((a & 0xff) << 24) | ((b & 0xff) << 16) |
((c & 0xff) << 8) | (d & 0xff); ((c & 0xff) << 8) | (d & 0xff);
return 1; return 1;
@@ -442,6 +445,7 @@ void
UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest) UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest)
{ {
dest->family = ntohs(src->family); dest->family = ntohs(src->family);
dest->_pad = 0;
switch (dest->family) { switch (dest->family) {
case IPADDR_INET4: case IPADDR_INET4:
@@ -554,7 +558,7 @@ char *UTI_SockaddrToString(struct sockaddr *sa)
{ {
unsigned short port; unsigned short port;
IPAddr ip; IPAddr ip;
char *result; char *result, *sun_path;
result = NEXT_BUFFER; result = NEXT_BUFFER;
@@ -567,7 +571,11 @@ char *UTI_SockaddrToString(struct sockaddr *sa)
snprintf(result, BUFFER_LENGTH, "%s:%hu", UTI_IPToString(&ip), port); snprintf(result, BUFFER_LENGTH, "%s:%hu", UTI_IPToString(&ip), port);
break; break;
case AF_UNIX: case AF_UNIX:
snprintf(result, BUFFER_LENGTH, "%s", ((struct sockaddr_un *)sa)->sun_path); sun_path = ((struct sockaddr_un *)sa)->sun_path;
snprintf(result, BUFFER_LENGTH, "%.*s", BUFFER_LENGTH - 1, sun_path);
/* Indicate truncated path */
if (strlen(sun_path) >= BUFFER_LENGTH)
result[BUFFER_LENGTH - 2] = '>';
break; break;
default: default:
snprintf(result, BUFFER_LENGTH, "[UNKNOWN]"); snprintf(result, BUFFER_LENGTH, "[UNKNOWN]");
@@ -754,8 +762,11 @@ UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
{ {
uint32_t ntp_sec, ntp_frac; uint32_t ntp_sec, ntp_frac;
/* As yet, there is no need to check for zero - all processing that /* Zero is a special value */
has to detect that case is in the NTP layer */ if (UTI_IsZeroNtp64(src)) {
UTI_ZeroTimespec(dest);
return;
}
ntp_sec = ntohl(src->hi); ntp_sec = ntohl(src->hi);
ntp_frac = ntohl(src->lo); ntp_frac = ntohl(src->lo);
@@ -767,9 +778,7 @@ UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
dest->tv_sec = ntp_sec - JAN_1970; dest->tv_sec = ntp_sec - JAN_1970;
#endif #endif
dest->tv_nsec = ntp_frac / NSEC_PER_NTP64 + 0.5; dest->tv_nsec = ntp_frac / NSEC_PER_NTP64;
UTI_NormaliseTimespec(dest);
} }
/* ================================================== */ /* ================================================== */
@@ -829,12 +838,11 @@ UTI_Log2ToDouble(int l)
void void
UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest) UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
{ {
uint32_t sec_low; uint32_t sec_low, nsec;
#ifdef HAVE_LONG_TIME_T #ifdef HAVE_LONG_TIME_T
uint32_t sec_high; uint32_t sec_high;
#endif #endif
dest->tv_nsec = ntohl(src->tv_nsec);
sec_low = ntohl(src->tv_sec_low); sec_low = ntohl(src->tv_sec_low);
#ifdef HAVE_LONG_TIME_T #ifdef HAVE_LONG_TIME_T
sec_high = ntohl(src->tv_sec_high); sec_high = ntohl(src->tv_sec_high);
@@ -846,7 +854,8 @@ UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
dest->tv_sec = sec_low; dest->tv_sec = sec_low;
#endif #endif
UTI_NormaliseTimespec(dest); nsec = ntohl(src->tv_nsec);
dest->tv_nsec = CLAMP(0U, nsec, 999999999U);
} }
/* ================================================== */ /* ================================================== */
@@ -1031,25 +1040,25 @@ create_dir(char *p, mode_t mode, uid_t uid, gid_t gid)
if (status < 0) { if (status < 0) {
if (errno != ENOENT) { if (errno != ENOENT) {
LOG(LOGS_ERR, LOGF_Util, "Could not access %s : %s", p, strerror(errno)); LOG(LOGS_ERR, "Could not access %s : %s", p, strerror(errno));
return 0; return 0;
} }
} else { } else {
if (S_ISDIR(buf.st_mode)) if (S_ISDIR(buf.st_mode))
return 1; return 1;
LOG(LOGS_ERR, LOGF_Util, "%s is not directory", p); LOG(LOGS_ERR, "%s is not directory", p);
return 0; return 0;
} }
/* Create the directory */ /* Create the directory */
if (mkdir(p, mode) < 0) { if (mkdir(p, mode) < 0) {
LOG(LOGS_ERR, LOGF_Util, "Could not create directory %s : %s", p, strerror(errno)); LOG(LOGS_ERR, "Could not create directory %s : %s", p, strerror(errno));
return 0; return 0;
} }
/* Set its owner */ /* Set its owner */
if (chown(p, uid, gid) < 0) { if (chown(p, uid, gid) < 0) {
LOG(LOGS_ERR, LOGF_Util, "Could not change ownership of %s : %s", p, strerror(errno)); LOG(LOGS_ERR, "Could not change ownership of %s : %s", p, strerror(errno));
/* Don't leave it there with incorrect ownership */ /* Don't leave it there with incorrect ownership */
rmdir(p); rmdir(p);
return 0; return 0;
@@ -1118,27 +1127,27 @@ UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
struct stat buf; struct stat buf;
if (stat(path, &buf)) { if (stat(path, &buf)) {
LOG(LOGS_ERR, LOGF_Util, "Could not access %s : %s", path, strerror(errno)); LOG(LOGS_ERR, "Could not access %s : %s", path, strerror(errno));
return 0; return 0;
} }
if (!S_ISDIR(buf.st_mode)) { if (!S_ISDIR(buf.st_mode)) {
LOG(LOGS_ERR, LOGF_Util, "%s is not directory", path); LOG(LOGS_ERR, "%s is not directory", path);
return 0; return 0;
} }
if ((buf.st_mode & 0777) & ~perm) { if ((buf.st_mode & 0777) & ~perm) {
LOG(LOGS_ERR, LOGF_Util, "Wrong permissions on %s", path); LOG(LOGS_ERR, "Wrong permissions on %s", path);
return 0; return 0;
} }
if (buf.st_uid != uid) { if (buf.st_uid != uid) {
LOG(LOGS_ERR, LOGF_Util, "Wrong owner of %s (%s != %d)", path, "UID", uid); LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "UID", uid);
return 0; return 0;
} }
if (buf.st_gid != gid) { if (buf.st_gid != gid) {
LOG(LOGS_ERR, LOGF_Util, "Wrong owner of %s (%s != %d)", path, "GID", gid); LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "GID", gid);
return 0; return 0;
} }
@@ -1152,17 +1161,17 @@ UTI_DropRoot(uid_t uid, gid_t gid)
{ {
/* Drop supplementary groups */ /* Drop supplementary groups */
if (setgroups(0, NULL)) if (setgroups(0, NULL))
LOG_FATAL(LOGF_Util, "setgroups() failed : %s", strerror(errno)); LOG_FATAL("setgroups() failed : %s", strerror(errno));
/* Set effective, saved and real group ID */ /* Set effective, saved and real group ID */
if (setgid(gid)) if (setgid(gid))
LOG_FATAL(LOGF_Util, "setgid(%d) failed : %s", gid, strerror(errno)); LOG_FATAL("setgid(%d) failed : %s", gid, strerror(errno));
/* Set effective, saved and real user ID */ /* Set effective, saved and real user ID */
if (setuid(uid)) if (setuid(uid))
LOG_FATAL(LOGF_Util, "setuid(%d) failed : %s", uid, strerror(errno)); LOG_FATAL("setuid(%d) failed : %s", uid, strerror(errno));
DEBUG_LOG(LOGF_Util, "Dropped root privileges: UID %d GID %d", uid, gid); DEBUG_LOG("Dropped root privileges: UID %d GID %d", uid, gid);
} }
/* ================================================== */ /* ================================================== */
@@ -1177,18 +1186,51 @@ UTI_GetRandomBytesUrandom(void *buf, unsigned int len)
if (!f) if (!f)
f = fopen(DEV_URANDOM, "r"); f = fopen(DEV_URANDOM, "r");
if (!f) if (!f)
LOG_FATAL(LOGF_Util, "Can't open %s : %s", DEV_URANDOM, strerror(errno)); LOG_FATAL("Can't open %s : %s", DEV_URANDOM, strerror(errno));
if (fread(buf, 1, len, f) != len) if (fread(buf, 1, len, f) != len)
LOG_FATAL(LOGF_Util, "Can't read from %s", DEV_URANDOM); LOG_FATAL("Can't read from %s", DEV_URANDOM);
} }
/* ================================================== */ /* ================================================== */
#ifdef HAVE_GETRANDOM
static void
get_random_bytes_getrandom(char *buf, unsigned int len)
{
static char rand_buf[256];
static unsigned int available = 0, disabled = 0;
unsigned int i;
for (i = 0; i < len; i++) {
if (!available) {
if (disabled)
break;
if (getrandom(rand_buf, sizeof (rand_buf), 0) != sizeof (rand_buf)) {
disabled = 1;
break;
}
available = sizeof (rand_buf);
}
buf[i] = rand_buf[--available];
}
if (i < len)
UTI_GetRandomBytesUrandom(buf, len);
}
#endif
/* ================================================== */
void void
UTI_GetRandomBytes(void *buf, unsigned int len) UTI_GetRandomBytes(void *buf, unsigned int len)
{ {
#ifdef HAVE_ARC4RANDOM #ifdef HAVE_ARC4RANDOM
arc4random_buf(buf, len); arc4random_buf(buf, len);
#elif defined(HAVE_GETRANDOM)
get_random_bytes_getrandom(buf, len);
#else #else
UTI_GetRandomBytesUrandom(buf, len); UTI_GetRandomBytesUrandom(buf, len);
#endif #endif