Compare commits

..

57 Commits

Author SHA1 Message Date
Miroslav Lichvar
dc52b61dad doc: update NEWS 2015-04-27 12:58:19 +02:00
Miroslav Lichvar
bbf4c3186b doc: update chrony description 2015-04-27 12:58:19 +02:00
Miroslav Lichvar
f72016a78e doc: document when smoothtime function is activated 2015-04-27 12:27:55 +02:00
Miroslav Lichvar
29b587a9c5 sys: fix TMX_ResetOffset() to set status back correctly 2015-04-27 12:27:55 +02:00
Miroslav Lichvar
cec4f2b140 reference: use 2012 leap second in leapsectz test 2015-04-24 12:16:47 +02:00
Miroslav Lichvar
05278c3b4c sources: ignore reselect distance when combining with refclock 2015-04-20 12:59:12 +02:00
Miroslav Lichvar
1769b8ea0f use return to exit from main function 2015-04-17 17:34:02 +02:00
Miroslav Lichvar
5686bd87d7 client: improve usage line 2015-04-17 17:33:38 +02:00
Miroslav Lichvar
1cda2db45d main: print usage with -h option 2015-04-17 17:30:38 +02:00
Miroslav Lichvar
fdf9640349 ntp: don't log error when socket() fails for client only socket 2015-04-14 15:59:55 +02:00
Miroslav Lichvar
8f2d5d99f1 doc: don't mention ancient systems
Also, don't try to track working versions of supported systems, assume
current versions are ok.
2015-04-13 17:18:19 +02:00
Miroslav Lichvar
61272e7ce8 update copyright years 2015-04-10 11:06:32 +02:00
Miroslav Lichvar
88b76f49cc doc: warn that unauthenticated peers are vulnerable to DoS attack 2015-04-10 10:52:30 +02:00
Miroslav Lichvar
ad942e352d sys: clamp frequency set in generic driver on exit 2015-04-10 10:22:28 +02:00
Miroslav Lichvar
39c2bcd462 util: don't allow time too close to 32-bit time_t overflow
In UTI_IsTimeOffsetSane() consider time in one year interval before
32-bit time_t overflow (in 2038) as invalid. Hopefully everything will
be using 64-bit time_t when that time comes.
2015-04-10 10:05:15 +02:00
Miroslav Lichvar
ae10664b24 doc: fix CVE-ID in NEWS
CVE-2015-1853 is for chrony, CVE-2015-1799 is for ntp.
2015-04-08 08:44:42 +02:00
Miroslav Lichvar
074dac4195 doc: update NEWS 2015-04-07 16:14:09 +02:00
Miroslav Lichvar
a8239b865a Merge branch '1.31-security'
Conflicts:
	NEWS
	ntp_core.c
2015-04-07 15:34:39 +02:00
Miroslav Lichvar
f6a9c5c1b7 sys: allow drivers to fail when applying step offset
Different systems may consider different time values to be valid.
Don't exit on settimeofday()/adjtimex() error in case the check in
UTI_IsTimeOffsetSane() isn't restrictive enough.
2015-04-07 15:23:47 +02:00
Miroslav Lichvar
42774ee851 refclock: check offset sanity 2015-04-07 15:23:47 +02:00
Miroslav Lichvar
4e26f48781 manual: check offset sanity 2015-04-07 15:23:47 +02:00
Miroslav Lichvar
aec97397e8 local: check offset sanity before accumulation
Don't accept an offset that points to time before 1970 or outside the
interval to which is mapped NTP time.
2015-04-07 15:23:47 +02:00
Miroslav Lichvar
183a648d01 local: clamp frequency offset
Don't allow frequency offset larger than 50%, the tracked time must not
stop or run backwards.
2015-04-07 14:13:41 +02:00
Miroslav Lichvar
27f8ad7fd1 cmdmon: fix handling of client access command
Rework the loop to limit the number of iterations to MAX_CLIENT_ACCESSES
and not waste CPU.
2015-04-07 14:07:40 +02:00
Miroslav Lichvar
a79fbef21e ntp: set maximum allowed polling interval
To have an upper bound don't allow polling interval be larger than 24
(194 days).
2015-04-07 14:06:53 +02:00
Miroslav Lichvar
565976acbe doc: document smoothtime directive 2015-04-07 12:38:37 +02:00
Miroslav Lichvar
54bbd2b1c0 doc: update NEWS 2015-04-07 11:09:08 +02:00
Miroslav Lichvar
10b2b53aa7 cmdmon: fix initialization of allocated reply slots
When allocating memory to save unacknowledged replies to authenticated
command requests, the last "next" pointer was not initialized to NULL.
When all allocated reply slots were used, the next reply could be
written to an invalid memory instead of allocating a new slot for it.

An attacker that has the command key and is allowed to access cmdmon
(only localhost is allowed by default) could exploit this to crash
chronyd or possibly execute arbitrary code with the privileges of the
chronyd process.
2015-04-07 11:09:02 +02:00
Miroslav Lichvar
e18ee0bb46 addrfilt: fix access configuration with subnet size indivisible by 4
When NTP or cmdmon access was configured (from chrony.conf or via
authenticated cmdmon) with a subnet size that is indivisible by 4 and
an address that has nonzero bits in the 4-bit subnet remainder (e.g.
192.168.15.0/22 or f000::/3), the new setting was written to an
incorrect location, possibly outside the allocated array.

An attacker that has the command key and is allowed to access cmdmon
(only localhost is allowed by default) could exploit this to crash
chronyd or possibly execute arbitrary code with the privileges of the
chronyd process.
2015-04-07 11:08:30 +02:00
Miroslav Lichvar
857d51ea8e test: extend 113-leapsecond for leap smear 2015-04-07 10:51:07 +02:00
Miroslav Lichvar
ba85544611 ntp: smear leap second with slewing mode and smoothing
Suppress leap second in packets sent to clients when smoothing and leap
second slew mode are enabled.
2015-04-07 10:45:32 +02:00
Miroslav Lichvar
293806d52d test: add 119-smoothtime 2015-04-07 10:42:32 +02:00
Miroslav Lichvar
7f45eb7957 ntp: add server time smoothing
Time smoothing determines an offset that needs to be applied to the
cooked time to make it smooth for external observers. Observed offset
and frequency change slowly and there are no discontinuities. This can
be used on an NTP server to make it easier for the clients to track the
time and keep their clocks close together even when large offset or
frequency corrections are applied to the server's clock (e.g. after
being offline for longer time).

Accumulated offset and frequency are smoothed out in three stages. In
the first stage, the frequency is changed at a constant rate (wander) up
to a maximum, in the second stage the frequency stays at the maximum for
as long as needed and in the third stage the frequency is brought back
to zero.

Time smoothing is configured by the smoothtime directive. It takes two
arguments, maximum frequency offset and maximum wander. It's disabled by
default.
2015-04-07 10:42:26 +02:00
Miroslav Lichvar
f0c48680fe ntp: protect authenticated symmetric associations against DoS attacks
An attacker knowing that NTP hosts A and B are peering with each other
(symmetric association) can send a packet with random timestamps to host
A with source address of B which will set the NTP state variables on A
to the values sent by the attacker. Host A will then send on its next
poll to B a packet with originate timestamp that doesn't match the
transmit timestamp of B and the packet will be dropped. If the attacker
does this periodically for both hosts, they won't be able to synchronize
to each other. It is a denial-of-service attack.

According to [1], NTP authentication is supposed to protect symmetric
associations against this attack, but in the NTPv3 (RFC 1305) and NTPv4
(RFC 5905) specifications the state variables are updated before the
authentication check is performed, which means the association is
vulnerable to the attack even when authentication is enabled.

To fix this problem in chrony, save the originate and local timestamps
only when the authentication check (test5) passed.

[1] https://www.eecis.udel.edu/~mills/onwire.html
2015-04-03 10:48:56 +02:00
Miroslav Lichvar
78283dd822 test: fix source selection check
The chronyd log message changed from "no reachable sources" to "no
selectable sources" in 8f062454.
2015-04-02 16:43:25 +02:00
Miroslav Lichvar
bbdf708d1a reference: update our reference time on slew 2015-03-31 11:51:03 +02:00
Miroslav Lichvar
08195d7e41 sourcestats: fix updating of slope on slew with large residual freq 2015-03-27 10:37:55 +01:00
Miroslav Lichvar
9ff0dbb7a4 test: make 009-sourceselection more reliable 2015-03-27 10:37:55 +01:00
Miroslav Lichvar
6ba97f9161 test: add 118-maxdelay 2015-03-27 10:37:55 +01:00
Miroslav Lichvar
4eca60e7dc test: add 117-fallbackdrift 2015-03-27 10:37:55 +01:00
Miroslav Lichvar
2af6f8cf78 reference: schedule fallback drift even when synchronized
After update to NTPv4 the synchronized status doesn't change when
sources are unreachable, start fallbackdrift timeout on reference update
too.
2015-03-27 10:37:54 +01:00
Miroslav Lichvar
d9a84d24cf reference: don't limit fallback drift offset 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
09ce631e21 reference: fix initial fallback drift setting 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
f93f2a15af ntp: check also reference timestamp in test3
Zero reference timestamp doesn't pass test7, but only before we reach
the next NTP era.
2015-03-27 10:37:54 +01:00
Miroslav Lichvar
47839b7701 cmdmon: remove obsolete request/reply in candm.h 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
41e99afe54 cmdmon: fix noselect flag setting in source data 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
80af04040a ntp: change default maxdelay to 3 seconds 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
3caa1e2f71 doc: document leapsecmode directive 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
ddbbe30b9e test: extend 113-leapsecond to test new leap modes 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
802a98e7fc reference: use step leap mode by default if system is not supported 2015-03-27 10:37:54 +01:00
Miroslav Lichvar
bb21841659 reference: update leap status right after leap second
Don't wait for the next update, there may not be any before the end of
the day.
2015-03-27 10:37:54 +01:00
Miroslav Lichvar
3f9691baff reference: don't report synchronized status during leap second
During inserted leap second the time is invalid, reply with
unsynchronized status to avoid confusing clients that are not smart
enough to ignore measurements close to leap second.
2015-03-27 10:37:54 +01:00
Miroslav Lichvar
f8db832491 reference: add new leap second handling modes
In addition to the system driver handling add new modes to slew or step
the system clock for leap second, or ignore it completely. This can be
configured with leapsecmode directive.
2015-03-27 10:37:48 +01:00
Miroslav Lichvar
c68a92ba80 sys: avoid syslog message when leap bits are not changed
After leap second the kernel removes STA_INS and STA_DEL bits from the
adjtimex status automatically, don't report a change when clearing the
bits.
2015-03-25 15:32:05 +01:00
Miroslav Lichvar
e5cf4645fe refclock: start refid numbering at zero
Commit d92583ed inadvertently changed the refclock refid numbering to
start from 1 instead of 0. Restore the original numbering.
2015-02-17 10:33:03 +01:00
Miroslav Lichvar
fad97e12da ntp: fix maxdelayratio test
This was broken in commit 8fbfe55e.
2015-01-29 12:49:02 +01:00
Miroslav Lichvar
2f6152a580 test: require latest clknetsim 2015-01-28 09:09:08 +01:00
47 changed files with 1238 additions and 247 deletions

View File

@@ -40,7 +40,7 @@ HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o mkdirpp.o \
reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
sys.o tempcomp.o util.o $(HASH_OBJ)
sys.o smooth.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@

23
NEWS
View File

@@ -5,6 +5,8 @@ Enhancements
------------
* Update to NTP version 4 (RFC 5905)
* Add pool directive to specify pool of NTP servers
* Add leapsecmode directive to select how to correct clock for leap second
* Add smoothtime directive to smooth served time and enable leap smear
* Add minsources directive to set required number of selectable sources
* Add minsamples and maxsamples options for all sources
* Add tempcomp configuration with list of points
@@ -13,8 +15,9 @@ Enhancements
* Improve source selection
* Handle offline sources as unreachable
* Open NTP server port only when necessary (client access is allowed by
allow directive/command, peer or broadcast is configured)
allow directive/command or peer/broadcast is configured)
* Change default bindcmdaddress to loopback address
* Change default maxdelay to 3 seconds
* Change default stratumweight to 0.001
* Update adjtimex synchronisation status
* Use system headers for adjtimex
@@ -25,9 +28,21 @@ Enhancements
Bug fixes
---------
* Fix accepting requests from configured sources when acquisitionport
is equal to server port
* Fix allocation of slots saving replies to authenticated commands
* Add sanity checks for time and frequency offset
* Don't report synchronised status during leap second
* Don't combine reference clocks with close NTP sources
* Fix accepting requests from configured sources
* Fix initial fallback drift setting
New in version 1.31.1
=====================
Security fixes
--------------
* Protect authenticated symmetric NTP associations against DoS attacks
(CVE-2015-1853)
* Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821)
* Fix initialization of reply slots for authenticated commands (CVE-2015-1822)
New in version 1.31
===================

81
README
View File

@@ -3,67 +3,35 @@ This is the README for chrony.
What is chrony?
===============
Chrony is a pair of programs for maintaining the accuracy of computer
clocks.
chrony is a versatile implementation of the Network Time Protocol (NTP).
It can synchronize the system clock with NTP servers, reference clocks
(e.g. GPS receiver), and manual input using wristwatch and keyboard.
It can also operate as an NTPv4 (RFC 5905) server and peer to provide
a time service to other computers in the network.
chronyd is a (background) daemon program that can be started at boot
time. This does most of the work.
It is designed to perform well in a wide range of conditions, including
intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
chronyc is a command-line interface program which can be used to
monitor chronyd's performance and to change various operating
parameters whilst it is running.
chronyd's main function is to obtain measurements of the true (UTC)
time from one of several sources, and correct the system clock
accordingly. It also works out the rate at which the system clock
gains or loses time and uses this information to keep it accurate
between measurements from the reference.
The reference time can be derived from Network Time Protocol (NTP)
servers, reference clocks, or wristwatch-and-keyboard (via chronyc).
The main source of information about the Network Time Protocol is
http://www.ntp.org.
It is designed so that it can work on computers which only have
intermittent access to reference sources, for example computers which
use a dial-up account to access the Internet or laptops. Of course, it
will work well on computers with permanent connections too.
In addition, on Linux it can monitor the system's real time clock
performance, so the system can maintain accurate time even across
reboots.
Typical accuracies available between 2 machines are
On an ethernet LAN : 100-200 microseconds, often much better
On a V32bis dial-up modem connection : 10's of milliseconds (from one
session to the next)
With a good reference clock the accuracy can reach one microsecond.
chronyd can also operate as an NTPv4 (RFC 5905) server, peer and broadcast
server.
Typical accuracy between two machines on a LAN is in tens, or a few
hundreds, of microseconds; over the Internet, accuracy is typically
within a few milliseconds. With a good hardware reference clock
sub-microsecond accuracy is possible.
Two programs are included in chrony, chronyd is a daemon that can be
started at boot time and chronyc is a command-line interface program
which can be used to monitor chronyd's performance and to change various
operating parameters whilst it is running.
What will chrony run on?
========================
Chrony can be successfully built and run on
1. Linux 2.2.x, 2.3.x, 2.4.x, 2.6.x, 3.x
2. Solaris 2.5/2.5.1/2.6/2.7/2.8 (various platforms)
3. SunOS 4.1.4 (Sparc 2 and Sparc 20)
4. BSD/386 v1.1 has been reported to work using the SunOS 4.1 driver.
5. NetBSD.
Any other system will require a porting exercise. You would need to
start from one of the existing system-specific drivers and look into
the quirks of certain system calls and the kernel on your target
system.
The software is known to work on Linux, FreeBSD, NetBSD and Solaris.
Closely related systems may work too. Any other system will likely
require a porting exercise. You would need to start from one of the
existing system-specific drivers and look into the quirks of certain
system calls and the kernel on your target system.
How do I set it up?
===================
@@ -116,6 +84,11 @@ chrony-dev-request@chrony.tuxfamily.org
as applicable.
License
=======
chrony is distributed under the GNU General Public License version 2.
Author
======

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997,1998,1999,2000,2001,2002,2005
* Copyright (C) Miroslav Lichvar 2009
* Copyright (C) Miroslav Lichvar 2009, 2015
*
* 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
@@ -199,7 +199,10 @@ set_subnet(TableNode *start_node,
/* How many subnet entries to set : 1->8, 2->4, 3->2 */
N = 1 << (NBITS-bits_to_go);
subnet = get_subnet(ip, bits_consumed);
subnet = get_subnet(ip, bits_consumed) & ~(N - 1);
assert(subnet + N <= TABLE_SIZE);
if (!(node->extended)) {
open_node(node);
}

11
candm.h
View File

@@ -300,11 +300,6 @@ typedef struct {
int32_t EOR;
} REQ_CycleLogs;
typedef struct {
IPAddr ip;
uint32_t bits_specd;
} REQ_SubnetsAccessed_Subnet;
/* This is based on the response size rather than the
request size */
#define MAX_CLIENT_ACCESSES 8
@@ -589,12 +584,6 @@ typedef struct {
int32_t EOR;
} RPY_ManualTimestamp;
typedef struct {
IPAddr ip;
uint32_t bits_specd;
uint32_t bitmap[8];
} RPY_SubnetsAccessed_Subnet;
typedef struct {
IPAddr ip;
uint32_t client_hits;

View File

@@ -56,34 +56,30 @@ Copyright @copyright{} 2009-2014 Miroslav Lichvar
@c {{{ S:Overview
@node Overview
@section Overview
Chrony is a software package for maintaining the accuracy of computer
system clocks. It consists of a pair of programs :
chrony is a versatile implementation of the Network Time Protocol (NTP).
It can synchronize the system clock with NTP servers, reference clocks
(e.g. GPS receiver), and manual input using wristwatch and keyboard.
It can also operate as an NTPv4 (RFC 5905) server and peer to provide
a time service to other computers in the network.
@itemize @bullet
@item @code{chronyd}. This is a daemon which runs in background on the
system. It obtains measurements (e.g. via the network) of the system's
offset relative to other systems, and adjusts the system time
accordingly. For isolated systems, the user can periodically enter the
correct time by hand (using @code{chronyc}). In either case,
@code{chronyd} determines the rate at which the computer gains or loses
time, and compensates for this.
It is designed to perform well in a wide range of conditions, including
intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
@code{chronyd} can also act as an NTP server, and provide a time-of-day service
to other computers. A typical set-up is to run @code{chronyd} on a gateway
computer that has a dial-up link to the Internet, and use it to serve time to
computers on a private LAN sitting behind the gateway. The IP addresses that
can act as clients of @code{chronyd} can be tightly controlled. The default is
no client access.
Typical accuracy between two machines on a LAN is in tens, or a few
hundreds, of microseconds; over the Internet, accuracy is typically
within a few milliseconds. With a good hardware reference clock
sub-microsecond accuracy is possible.
@item @code{chronyc}. This is a command-line driven control and
monitoring program. An administrator can use this to fine-tune various
parameters within the daemon, add or delete servers etc whilst the
daemon is running.
Two programs are included in chrony, @code{chronyd} is a daemon that can
be started at boot time and @code{chronyc} is a command-line interface
program which can be used to monitor @code{chronyd}'s performance and to
change various operating parameters whilst it is running.
The IP addresses from which @code{chronyc} clients may connect can be tightly
controlled. The default is just the computer that @code{chronyd} itself is
running on.
@end itemize
@c }}}
@c {{{ S:Acknowledgments
@node Acknowledgements
@@ -123,24 +119,10 @@ different operating systems may provide different function calls to
achieve this, and even where the same function is used it may have
different quirks in its behaviour.
The software is known to work in the following environments:
@itemize @bullet
@item Linux 2.2 and newer
@item NetBSD
@item BSD/386
@item Solaris 2.3/2.5/2.5.1/2.6/2.7/2.8 on Sparc (Sparc 20, Ultrasparc) and
i386
@item SunOS 4.1.4 on Sparc 2 and Sparc20.
@end itemize
Closely related systems may work too, but they have not been tested.
Porting the software to other system (particularly to those supporting
an @code{adjtime} system call) should not be difficult, however it
requires access to such systems to test out the driver.
The software is known to work on Linux, FreeBSD, NetBSD and Solaris. Closely
related systems may work too. Porting the software to other systems
(particularly to those supporting an @code{adjtime} system call) should not be
difficult, however it requires access to such systems to test out the driver.
@c }}}
@c {{{ S:Other programs
@node Other time synchronisation packages
@@ -305,8 +287,7 @@ Particular areas that need addressing are :
@item Porting to other Unices
This involves creating equivalents of sys_solaris.c, sys_linux.c etc for the
new system. Note, the Linux driver has been reported as working on a range of
different architectures (Alpha, Sparc, MIPS as well as x86 of course).
new system.
@item Porting to Windows NT
@@ -1012,9 +993,8 @@ configuration file. This option is useful if you want to stop and
restart @code{chronyd} briefly for any reason, e.g. to install a new
version. However, it only makes sense on systems where the kernel can
maintain clock compensation whilst not under @code{chronyd's} control.
The only version where this happens so far is Linux. On systems where
this is not the case, e.g. Solaris and SunOS the option should not be
used.
The only version where this happens so far is Linux. On other systems
this option should not be used.
@item -R
When this option is used, the @code{initstepslew} directive and the
@code{makestep} directive used with a positive limit will be ignored.
@@ -1158,6 +1138,7 @@ the configuration file is ignored.
* include directive:: Include a configuration file
* initstepslew directive:: Trim the system clock on boot-up
* keyfile directive:: Specify location of file containing keys
* leapsecmode directive:: Select leap second handling mode
* leapsectz directive:: Read leap second data from tz database
* local directive:: Allow unsynchronised machine to act as server
* lock_all directive:: Require that chronyd be locked into RAM
@@ -1189,6 +1170,7 @@ the configuration file is ignored.
* rtcsync directive:: Specify that RTC should be automatically synchronised by kernel
* sched_priority directive:: Require real-time scheduling and specify a priority for it
* server directive:: Specify an NTP server
* smoothtime directive:: Smooth served time to keep clients close together
* stratumweight directive:: Specify how important is stratum when selecting source
* tempcomp directive:: Specify temperature sensor and compensation coefficients
* user directive:: Specify user for dropping root privileges
@@ -1636,14 +1618,13 @@ the program exits. (See the dumpdir command above).
@subsection fallbackdrift
Fallback drifts are long-term averages of the system clock drift
calculated over exponentially increasing intervals. They are used
when the clock is unsynchronised to avoid quickly drifting away from
true time if there was a short-term deviation in drift before the
synchronisation was lost.
when the clock is no longer synchronised to avoid quickly drifting
away from true time if there was a short-term deviation in the drift
before the synchronisation was lost.
The directive specifies the minimum and maximum interval for how long
the system clock has to be unsynchronised to switch between fallback
drifts. They are defined as a power of 2 (in seconds). The syntax is
as follows
The directive specifies the minimum and maximum interval since last
clock update to switch between fallback drifts. They are defined as a
power of 2 (in seconds). The syntax is as follows
@example
fallbackdrift 16 19
@@ -1651,13 +1632,13 @@ fallbackdrift 16 19
In this example, the minimum interval is 16 (18 hours) and maximum
interval is 19 (6 days). The system clock frequency will be set to
the first fallback 18 hours after the synchronisation was lost, to the
the first fallback 18 hours after last clock update, to the
second after 36 hours, etc. This might be a good setting to cover
daily and weekly temperature fluctuations.
By default (or if the specified maximum or minimum is 0), no fallbacks
will be used and the clock frequency will stay at the last value
calculated before synchronisation was lost.
are used and the clock frequency changes only with new measurements from
NTP, reference clocks or manual input.
@c }}}
@c {{{ generatecommandkey
@node generatecommandkey directive
@@ -1808,6 +1789,62 @@ The ID for the chronyc authentication key is specified with the commandkey
command (see earlier). The command key can be generated automatically on
start with the @code{generatecommandkey} directive.
@c }}}
@c {{{ leapsecmode
@node leapsecmode directive
@subsection leapsecmode
This directive selects how @code{chronyd} handles leap seconds. The Unix time
doesn't include leap seconds. When a leap second is applied to UTC, the system
clock is off by one second and it needs to be corrected.
There are four options:
@table @code
@item system
The kernel steps the system clock backwards by one second at 0:00:00 UTC
(before correction) when leap second is inserted or steps forward by one second
at 23:59:59 UTC when leap second is deleted. This is the default mode when the
system driver supports leap seconds (currently Linux only).
@item step
This is similar to the system mode, except the clock is stepped by
@code{chronyd} instead of the kernel. This is the default mode when the system
driver doesn't support leap seconds.
@item slew
The clock is corrected by slew starting at 0:00:00 UTC when leap second is
inserted or 23:59:59 UTC when leap second is deleted. This may be preferred
over the system or step mode when applications running on the system are
sensitive to jumps in the system time and it's acceptable that the clock will
be off for a longer time. On Linux with the default @code{maxslewrate} the
correction takes 12 seconds. Note that unless the @code{smoothtime} directive
is used (@pxref{smoothtime directive}), there will still be a jump in the time
that @code{chronyd} serves to NTP clients. With the @code{smoothtime}
directive, the leap second status will not be passed to NTP clients and the
leap second will be "smeared" instead.
@item ignore
No correction is applied to the clock for the leap second. The clock will be
corrected later in normal operation when new measurements are made and the
estimated offset includes the one second error.
@end table
An example of the command is
@example
leapsecmode slew
@end example
An example enabling the leap smear for NTP clients with the @code{smoothtime}
directive could be
@example
leapsecmode slew
smoothtime 400 0.001
@end example
With this configuration the NTP clients would not know there was any leap
second. The server time they follow would be slowly corrected in about 16
hours after the leap second was applied to UTC. This configuration should not
be used if the clients poll also other NTP servers, because they could reject
this server as a falseticker or could fail to select a source completely.
@c }}}
@c {{{ leapsectz
@node leapsectz directive
@subsection leapsectz
@@ -2486,6 +2523,21 @@ be reported using the @code{clients} command in @code{chronyc}.
The syntax of this directive is identical to that for the @code{server}
directive (@pxref{server directive}), except that it is used to specify
an NTP peer rather than an NTP server.
Please note that NTP peers that are not configured with a key to enable
authentication are vulnerable to a denial-of-service attack. An attacker
knowing that NTP hosts A and B are peering with each other can send a packet
with random timestamps to host A with source address of B which will set the
NTP state variables on A to the values sent by the attacker. Host A will then
send on its next poll to B a packet with originate timestamp that doesn't match
the transmit timestamp of B and the packet will be dropped. If the attacker
does this periodically for both hosts, they won't be able to synchronize to
each other.
This attack can be prevented by enabling authentication with the key option, or
using the @code{server} directive on both sides to specify the other host as a
server instead of peer, the only drawback is that it will double the network
traffic between the two hosts.
@c }}}
@c {{{ pidfile
@node pidfile directive
@@ -2873,7 +2925,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 command. For example, @code{maxdelay 0.3} would indicate that
measurements with a round-trip delay of 0.3 seconds or more should be
ignored.
ignored. The default value is 3 seconds.
@item maxdelayratio
This option is similar to the maxdelay option above. @code{chronyd}
@@ -2981,6 +3033,44 @@ Set the maximum number of samples kept for this source. This overrides the
@end table
@c }}}
@c {{{ smoothtime
@node smoothtime directive
@subsection smoothtime
The @code{smoothtime} directive can be used to enable smoothing of the time
that @code{chronyd} serves to its clients to make it easier for them to track
it and keep their clocks close together even when large offset or frequency
corrections are applied to the server's clock, for example after being offline
for a longer time.
If a large offset has been accumulated, it may take a very long time to smooth
it out. This directive should be used only when the clients are not configured
to poll also another NTP server, because they could reject this server as a
falseticker or fail to select a source completely.
The smoothing process is independent from any slewing applied to the local
system clock, but the accumulated offset and frequency for smoothing will be
reset when the clock is corrected by step, e.g. by the @code{makestep}
directive or command.
The directive takes two arguments, the maximum frequency offset of the smoothed
time to the tracked NTP time (in ppm) and the maximum rate at which the
frequency offset is allowed to change (in ppm per second). The smoothing
process is activated when 1/10000 of the estimated skew of the local clock
falls below the maximum rate of frequency change.
An example suitable for clients using @code{ntpd} and 1024 second polling
interval could be
@example
smoothtime 400 0.001
@end example
An example suitable for clients using @code{chronyd} on Linux could be
@example
smoothtime 50000 0.01
@end example
@c }}}
@c {{{ stratumweight
@node stratumweight directive
@subsection stratumweight
@@ -4072,9 +4162,7 @@ password
@end example
The computer will respond with a @samp{Password:} prompt, at which you
should enter the password and press return. (Note that the no-echo mode
is limited to 8 characters on SunOS 4.1 due to limitations in the system
library. Other systems do not have this restriction.)
should enter the password and press return.
The password can be encoded as a string of characters not containing a space
with optional @code{ASCII:} prefix or as a hexadecimal number with @code{HEX:}
@@ -4458,7 +4546,7 @@ true time (which it reports to NTP clients when it is operating in
server mode). The value reported on this line is the difference due to
this effect.
On systems such as Solaris and SunOS, @code{chronyd} has no means to
On systems other than Linux, @code{chronyd} doesn't
adjust the fundamental rate of the system clock, so keeps the system
time correct by periodically making offsets to it as though an error had
been measured. The build up of these offsets will be observed in this

View File

@@ -70,8 +70,7 @@ option is useful if you want to stop and restart \fBchronyd\fR briefly for any
reason, e.g. to install a new version. However, it only makes sense on
systems where the kernel can maintain clock compensation whilst not under
\fBchronyd\fR's control. The only version where this happens so far is Linux.
On systems where this is not the case, e.g. Solaris and SunOS the option
should not be used.
On other systems this option should not be used.
.TP
.B \-R
When this option is used, the \fIinitstepslew\fR directive and the

View File

@@ -2729,10 +2729,11 @@ main(int argc, char **argv)
family = IPADDR_INET6;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
exit(0);
return 0;
} else if (!strncmp(*argv, "-", 1)) {
fprintf(stderr, "Usage : %s [-h <hostname>] [-p <port-number>] [-n] [-4|-6] [-m] [-a] [-f <file>]] [command]\n", progname);
exit(1);
fprintf(stderr, "Usage: %s [-h HOST] [-p PORT] [-n] [-4|-6] [-a] [-f FILE] [-m] [COMMAND]\n",
progname);
return 1;
} else {
break; /* And process remainder of line as a command */
}

View File

@@ -926,14 +926,16 @@ handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
long offset_cs;
double dfreq_ppm, new_afreq_ppm;
UTI_TimevalNetworkToHost(&rx_message->data.settime.ts, &ts);
if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
if (!MNL_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED);
} else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
tx_message->status = htons(STT_SUCCESS);
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP);
tx_message->data.manual_timestamp.centiseconds = htonl((int32_t)offset_cs);
tx_message->data.manual_timestamp.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm);
tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm);
} else {
tx_message->status = htons(STT_NOTENABLED);
tx_message->status = htons(STT_FAILED);
}
}
@@ -1051,7 +1053,7 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.flags = htons(RPY_SD_FLAG_PREFER);
break;
case RPT_NOSELECT:
tx_message->data.source_data.flags = htons(RPY_SD_FLAG_PREFER);
tx_message->data.source_data.flags = htons(RPY_SD_FLAG_NOSELECT);
break;
}
tx_message->data.source_data.reachability = htons(report.reachability);
@@ -1463,7 +1465,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
{
CLG_Status result;
RPT_ClientAccessByIndex_Report report;
unsigned long first_index, n_indices, last_index, n_indices_in_table;
unsigned long first_index, n_indices, n_indices_in_table;
int i, j;
struct timeval now;
@@ -1471,16 +1473,15 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
first_index = ntohl(rx_message->data.client_accesses_by_index.first_index);
n_indices = ntohl(rx_message->data.client_accesses_by_index.n_indices);
last_index = first_index + n_indices - 1;
if (n_indices > MAX_CLIENT_ACCESSES)
n_indices = MAX_CLIENT_ACCESSES;
tx_message->status = htons(STT_SUCCESS);
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX);
for (i = first_index, j = 0;
(i <= last_index) && (j < MAX_CLIENT_ACCESSES);
i++) {
result = CLG_GetClientAccessReportByIndex(i, &report, now.tv_sec, &n_indices_in_table);
for (i = 0, j = 0; i < n_indices; i++) {
result = CLG_GetClientAccessReportByIndex(first_index + i, &report,
now.tv_sec, &n_indices_in_table);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices_in_table);
switch (result) {
@@ -1506,7 +1507,7 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
}
}
tx_message->data.client_accesses_by_index.next_index = htonl(i);
tx_message->data.client_accesses_by_index.next_index = htonl(first_index + i);
tx_message->data.client_accesses_by_index.n_clients = htonl(j);
}
@@ -1556,8 +1557,11 @@ handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message)
{
LCL_MakeStep();
tx_message->status = htons(STT_SUCCESS);
if (!LCL_MakeStep()) {
tx_message->status = htons(STT_FAILED);
} else {
tx_message->status = htons(STT_SUCCESS);
}
}
/* ================================================== */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013
* Copyright (C) Miroslav Lichvar 2013-2014
*
* 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

60
conf.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2014
* Copyright (C) Miroslav Lichvar 2009-2015
*
* 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
@@ -63,6 +63,7 @@ static void parse_deny(char *);
static void parse_fallbackdrift(char *);
static void parse_include(char *);
static void parse_initstepslew(char *);
static void parse_leapsecmode(char *);
static void parse_local(char *);
static void parse_log(char *);
static void parse_mailonchange(char *);
@@ -72,6 +73,7 @@ static void parse_peer(char *);
static void parse_pool(char *);
static void parse_refclock(char *);
static void parse_server(char *);
static void parse_smoothtime(char *);
static void parse_tempcomp(char *);
/* ================================================== */
@@ -184,6 +186,10 @@ static IPAddr bind_cmd_address4, bind_cmd_address6;
* chronyds being started. */
static char *pidfile;
/* Smoothing constants */
static double smooth_max_freq = 0.0; /* in ppm */
static double smooth_max_wander = 0.0; /* in ppm/s */
/* Temperature sensor, update interval and compensation coefficients */
static char *tempcomp_sensor_file = NULL;
static char *tempcomp_point_file = NULL;
@@ -193,6 +199,9 @@ static double tempcomp_T0, tempcomp_k0, tempcomp_k1, tempcomp_k2;
static int sched_priority = 0;
static int lock_memory = 0;
/* Leap second handling mode */
static REF_LeapMode leapsec_mode = REF_LeapModeSystem;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leapsec_tz = NULL;
@@ -440,6 +449,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_initstepslew(p);
} else if (!strcasecmp(command, "keyfile")) {
parse_string(p, &keys_file);
} else if (!strcasecmp(command, "leapsecmode")) {
parse_leapsecmode(p);
} else if (!strcasecmp(command, "leapsectz")) {
parse_string(p, &leapsec_tz);
} else if (!strcasecmp(command, "linux_freq_scale")) {
@@ -506,6 +517,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_int(p, &sched_priority);
} else if (!strcasecmp(command, "server")) {
parse_server(p);
} else if (!strcasecmp(command, "smoothtime")) {
parse_smoothtime(p);
} else if (!strcasecmp(command, "stratumweight")) {
parse_double(p, &stratum_weight);
} else if (!strcasecmp(command, "tempcomp")) {
@@ -830,6 +843,23 @@ parse_initstepslew(char *line)
/* ================================================== */
static void
parse_leapsecmode(char *line)
{
if (!strcasecmp(line, "system"))
leapsec_mode = REF_LeapModeSystem;
else if (!strcasecmp(line, "slew"))
leapsec_mode = REF_LeapModeSlew;
else if (!strcasecmp(line, "step"))
leapsec_mode = REF_LeapModeStep;
else if (!strcasecmp(line, "ignore"))
leapsec_mode = REF_LeapModeIgnore;
else
command_parse_error();
}
/* ================================================== */
static void
parse_clientloglimit(char *line)
{
@@ -1141,6 +1171,17 @@ parse_broadcast(char *line)
/* ================================================== */
static void
parse_smoothtime(char *line)
{
check_number_of_args(line, 2);
if (sscanf(line, "%lf %lf", &smooth_max_freq, &smooth_max_wander) != 2) {
smooth_max_freq = 0.0;
command_parse_error();
}
}
/* ================================================== */
static void
parse_tempcomp(char *line)
{
@@ -1664,6 +1705,14 @@ CNF_GetPidFile(void)
/* ================================================== */
REF_LeapMode
CNF_GetLeapSecMode(void)
{
return leapsec_mode;
}
/* ================================================== */
char *
CNF_GetLeapSecTimezone(void)
{
@@ -1688,6 +1737,15 @@ CNF_GetLockMemory(void)
/* ================================================== */
void
CNF_GetSmooth(double *max_freq, double *max_wander)
{
*max_freq = smooth_max_freq;
*max_wander = smooth_max_wander;
}
/* ================================================== */
void
CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2)
{

3
conf.h
View File

@@ -29,6 +29,7 @@
#define GOT_CONF_H
#include "addressing.h"
#include "reference.h"
extern void CNF_Initialise(int restarted);
extern void CNF_Finalise(void);
@@ -75,6 +76,7 @@ extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
/* Value returned in ppm, as read from file */
@@ -94,6 +96,7 @@ extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
extern void CNF_GetSmooth(double *max_freq, double *max_wander);
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
extern char *CNF_GetUser(void);

91
local.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2014
* Copyright (C) Miroslav Lichvar 2011, 2014-2015
*
* 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
@@ -36,11 +36,16 @@
#include "local.h"
#include "localp.h"
#include "memory.h"
#include "smooth.h"
#include "util.h"
#include "logging.h"
/* ================================================== */
/* Maximum allowed frequency offset in ppm, the time must not stop
or run backwards */
#define MAX_FREQ 500000.0
/* Variable to store the current frequency, in ppm */
static double current_freq_ppm;
@@ -397,6 +402,33 @@ LCL_ReadAbsoluteFrequency(void)
}
/* ================================================== */
static double
clamp_freq(double freq)
{
if (freq <= MAX_FREQ && freq >= -MAX_FREQ)
return freq;
LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq);
return freq >= MAX_FREQ ? MAX_FREQ : -MAX_FREQ;
}
/* ================================================== */
static int
check_offset(struct timeval *now, double offset)
{
/* Check if the time will be still sane with accumulated offset */
if (UTI_IsTimeOffsetSane(now, -offset))
return 1;
LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset);
return 0;
}
/* ================================================== */
/* This involves both setting the absolute frequency with the
system-specific driver, as well as calling all notify handlers */
@@ -406,6 +438,8 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
struct timeval raw, cooked;
double dfreq;
afreq_ppm = clamp_freq(afreq_ppm);
/* Apply temperature compensation */
if (temp_comp_ppm != 0.0) {
afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm;
@@ -443,6 +477,8 @@ LCL_AccumulateDeltaFrequency(double dfreq)
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
current_freq_ppm = clamp_freq(current_freq_ppm);
/* Call the system-specific driver for setting the frequency */
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
@@ -467,6 +503,9 @@ LCL_AccumulateOffset(double offset, double corr_rate)
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, offset))
return;
(*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */
@@ -475,7 +514,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
/* ================================================== */
void
int
LCL_ApplyStepOffset(double offset)
{
struct timeval raw, cooked;
@@ -486,10 +525,21 @@ LCL_ApplyStepOffset(double offset)
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
(*drv_apply_step_offset)(offset);
if (!check_offset(&raw, offset))
return 0;
if (!(*drv_apply_step_offset)(offset)) {
LOG(LOGS_ERR, LOGF_Local, "Could not step clock");
return 0;
}
/* Reset smoothing on all clock steps */
SMT_Reset(&cooked);
/* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
return 1;
}
/* ================================================== */
@@ -506,6 +556,20 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
/* ================================================== */
void
LCL_NotifyLeap(int leap)
{
struct timeval raw, cooked;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
/* Dispatch to all handlers as if the clock was stepped */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep);
}
/* ================================================== */
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
@@ -517,6 +581,9 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
to the change we are about to make */
LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, doffset))
return;
old_freq_ppm = current_freq_ppm;
/* Work out new absolute frequency. Note that absolute frequencies
@@ -524,6 +591,8 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
terms of the gradient of the (offset) v (local time) function. */
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
current_freq_ppm = clamp_freq(current_freq_ppm);
DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
old_freq_ppm, current_freq_ppm, doffset);
@@ -587,9 +656,13 @@ LCL_MakeStep(void)
LCL_ReadRawTime(&raw);
LCL_GetOffsetCorrection(&raw, &correction, NULL);
if (!check_offset(&raw, -correction))
return 0;
/* Cancel remaining slew and make the step */
LCL_AccumulateOffset(correction, 0.0);
LCL_ApplyStepOffset(-correction);
if (!LCL_ApplyStepOffset(-correction))
return 0;
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
@@ -598,8 +671,16 @@ LCL_MakeStep(void)
/* ================================================== */
int
LCL_CanSystemLeap(void)
{
return drv_set_leap ? 1 : 0;
}
/* ================================================== */
void
LCL_SetLeap(int leap)
LCL_SetSystemLeap(int leap)
{
if (drv_set_leap) {
(drv_set_leap)(leap);

18
local.h
View File

@@ -159,13 +159,17 @@ extern void LCL_AccumulateOffset(double offset, double corr_rate);
the system clock is fast on true time, i.e. it needs to be stepped
backwards. (Same convention as for AccumulateOffset routine). */
extern void LCL_ApplyStepOffset(double offset);
extern int LCL_ApplyStepOffset(double offset);
/* Routine to invoke notify handlers on an unexpected time jump
in system clock */
extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
double offset, double dispersion);
/* Routine to invoke notify handlers on leap second when the system clock
doesn't correct itself */
extern void LCL_NotifyLeap(int leap);
/* Perform the combination of modifying the frequency and applying
a slew, in one easy step */
extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
@@ -194,10 +198,14 @@ extern void LCL_Finalise(void);
to a timezone problem. */
extern int LCL_MakeStep(void);
/* Routine to schedule a leap second. Leap second will be inserted
at the end of the day if argument is positive, deleted if negative,
and zero cancels scheduled leap second. */
extern void LCL_SetLeap(int leap);
/* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap
does something */
extern int LCL_CanSystemLeap(void);
/* Routine to set the system clock to correct itself for a leap second if
supported. Leap second will be inserted at the end of the day if the
argument is positive, deleted if negative, and zero resets the setting. */
extern void LCL_SetSystemLeap(int leap);
/* Routine to set a frequency correction (in ppm) that should be applied
to local clock to compensate for temperature changes. A positive

View File

@@ -47,7 +47,7 @@ typedef void (*lcl_AccrueOffsetDriver)(double offset, double corr_rate);
/* System driver to apply a step offset. A positive argument means step
the clock forwards. */
typedef void (*lcl_ApplyStepOffsetDriver)(double offset);
typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
/* System driver to convert a raw time to an adjusted (cooked) time.
The number of seconds returned in 'corr' have to be added to the

View File

@@ -100,7 +100,8 @@ typedef enum {
LOGF_SysWinnt,
LOGF_TempComp,
LOGF_RtcLinux,
LOGF_Refclock
LOGF_Refclock,
LOGF_Smooth,
} LOG_Facility;
/* Init function */

12
main.c
View File

@@ -49,6 +49,7 @@
#include "refclock.h"
#include "clientlog.h"
#include "nameserv.h"
#include "smooth.h"
#include "tempcomp.h"
/* ================================================== */
@@ -88,6 +89,7 @@ MAI_CleanupAndExit(void)
/* Don't update clock when removing sources */
REF_SetMode(REF_ModeIgnore);
SMT_Finalise();
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
@@ -345,6 +347,7 @@ int main
(int argc, char **argv)
{
const char *conf_file = DEFAULT_CONF_FILE;
const char *progname = argv[0];
char *user = NULL;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0;
@@ -384,7 +387,7 @@ int main
} 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);
exit(0);
return 0;
} else if (!strcmp("-n", *argv)) {
nofork = 1;
} else if (!strcmp("-d", *argv)) {
@@ -403,6 +406,10 @@ int main
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n",
progname);
return 0;
} else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else {
@@ -415,7 +422,7 @@ int main
if (getuid() != 0) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
fprintf(stderr,"Not superuser\n");
exit(1);
return 1;
}
/* Turn into a daemon */
@@ -495,6 +502,7 @@ int main
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();
SMT_Initialise();
/* From now on, it is safe to do finalisation on exit */
initialised = 1;

View File

@@ -54,6 +54,8 @@ typedef struct {
(measured-predicted)) */
} Sample;
#define MIN_SAMPLE_SEPARATION 1.0
#define MAX_SAMPLES 16
static Sample samples[16];
@@ -174,14 +176,24 @@ int
MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
{
struct timeval now;
double offset;
double offset, diff;
int i;
if (enabled) {
/* Check whether timestamp is within margin of old one */
LCL_ReadCookedTime(&now, NULL);
/* Make sure the provided timestamp is sane and the sample
is not too close to the last one */
if (!UTI_IsTimeOffsetSane(ts, 0.0))
return 0;
if (n_samples) {
UTI_DiffTimevalsToDouble(&diff, &now, &samples[n_samples - 1].when);
if (diff < MIN_SAMPLE_SEPARATION)
return 0;
}
UTI_DiffTimevalsToDouble(&offset, &now, ts);
/* Check if buffer full up */
@@ -258,6 +270,14 @@ MNL_Reset(void)
n_samples = 0;
}
/* ================================================== */
int
MNL_IsEnabled(void)
{
return enabled;
}
/* ================================================== */
/* Generate report data for the REQ_MANUAL_LIST command/monitoring
protocol */

View File

@@ -38,6 +38,7 @@ extern int MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfre
extern void MNL_Enable(void);
extern void MNL_Disable(void);
extern void MNL_Reset(void);
extern int MNL_IsEnabled(void);
extern void MNL_ReportSamples(RPT_ManualSamplesReport *report, int max, int *n);
extern int MNL_DeleteSample(int index);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2014
* Copyright (C) Miroslav Lichvar 2009-2015
*
* 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
@@ -36,6 +36,7 @@
#include "sched.h"
#include "reference.h"
#include "local.h"
#include "smooth.h"
#include "sources.h"
#include "util.h"
#include "conf.h"
@@ -216,8 +217,9 @@ static ARR_Instance broadcasts;
/* Invalid stratum number */
#define NTP_INVALID_STRATUM 0
/* Minimum allowed poll interval */
/* Minimum and maximum allowed poll interval */
#define MIN_POLL 0
#define MAX_POLL 24
/* Kiss-o'-Death codes */
#define KOD_RATE 0x52415445UL /* RATE */
@@ -444,9 +446,13 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->minpoll = params->minpoll;
if (result->minpoll < MIN_POLL)
result->minpoll = SRC_DEFAULT_MINPOLL;
else if (result->minpoll > MAX_POLL)
result->minpoll = MAX_POLL;
result->maxpoll = params->maxpoll;
if (result->maxpoll < MIN_POLL)
result->maxpoll = SRC_DEFAULT_MAXPOLL;
else if (result->maxpoll > MAX_POLL)
result->maxpoll = MAX_POLL;
if (result->maxpoll < result->minpoll)
result->maxpoll = result->minpoll;
@@ -757,14 +763,14 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
{
NTP_Packet message;
int leap, auth_len, length, ret;
struct timeval local_transmit;
struct timeval local_receive, local_transmit;
/* Parameters read from reference module */
int are_we_synchronised, our_stratum;
int are_we_synchronised, our_stratum, smooth_time;
NTP_Leap leap_status;
uint32_t our_ref_id, ts_fuzz;
struct timeval our_ref_time;
double our_root_delay, our_root_dispersion;
double our_root_delay, our_root_dispersion, smooth_offset;
/* Don't reply with version higher than ours */
if (version > NTP_VERSION) {
@@ -781,6 +787,20 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
&our_ref_id, &our_ref_time,
&our_root_delay, &our_root_dispersion);
/* Get current smoothing offset when sending packet to a client */
if (SMT_IsEnabled() && (my_mode == MODE_SERVER || my_mode == MODE_BROADCAST)) {
smooth_time = 1;
smooth_offset = SMT_GetOffset(&local_transmit);
/* Suppress leap second when smoothing and slew mode are enabled */
if (REF_GetLeapMode() == REF_LeapModeSlew &&
(leap_status == LEAP_InsertSecond || leap_status == LEAP_DeleteSecond))
leap_status = LEAP_Normal;
} else {
smooth_time = 0;
smooth_offset = 0.0;
}
if (are_we_synchronised) {
leap = (int) leap_status;
} else {
@@ -807,6 +827,14 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
message.reference_id = htonl((NTP_int32) our_ref_id);
/* Now fill in timestamps */
if (smooth_time) {
UTI_AddDoubleToTimeval(&our_ref_time, smooth_offset, &our_ref_time);
UTI_AddDoubleToTimeval(local_rx, smooth_offset, &local_receive);
} else {
local_receive = *local_rx;
}
UTI_TimevalToInt64(&our_ref_time, &message.reference_ts, 0);
/* Originate - this comes from the last packet the source sent us */
@@ -816,7 +844,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
This timestamp will have been adjusted so that it will now look to
the source like we have been running on our latest estimate of
frequency all along */
UTI_TimevalToInt64(local_rx, &message.receive_ts, 0);
UTI_TimevalToInt64(&local_receive, &message.receive_ts, 0);
/* Prepare random bits which will be added to the transmit timestamp. */
ts_fuzz = UTI_GetNTPTsFuzz(message.precision);
@@ -826,6 +854,9 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
from the source we're sending to now. */
LCL_ReadCookedTime(&local_transmit, NULL);
if (smooth_time)
UTI_AddDoubleToTimeval(&local_transmit, smooth_offset, &local_transmit);
length = NTP_NORMAL_PACKET_LENGTH;
/* Authenticate */
@@ -1151,6 +1182,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
association if not properly 'up'. */
test3 = (message->originate_ts.hi || message->originate_ts.lo) &&
(message->receive_ts.hi || message->receive_ts.lo) &&
(message->reference_ts.hi || message->reference_ts.lo) &&
(message->transmit_ts.hi || message->transmit_ts.lo);
/* Test 4 would check for denied access. It would always pass as this
@@ -1185,11 +1217,13 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
kod_rate = 1;
}
/* Regardless of any validity checks we apply, we are required to
save these fields from the packet into the ntp source instance record.
Note we can't do this assignment before test 1 has been carried out. */
inst->remote_orig = message->transmit_ts;
inst->local_rx = *now;
/* The transmit timestamp and local receive timestamp must not be saved when
the authentication test failed to prevent denial-of-service attacks on
symmetric associations using authentication */
if (test5) {
inst->remote_orig = message->transmit_ts;
inst->local_rx = *now;
}
/* This protects against replay of the last packet we sent */
if (test2)
@@ -1243,7 +1277,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
minimum one currently in the stats data register is less than an
administrator-defined value */
testB = inst->max_delay_ratio <= 1.0 ||
delay / SRC_MinRoundTripDelay(inst->source) > inst->max_delay_ratio;
delay / SRC_MinRoundTripDelay(inst->source) <= inst->max_delay_ratio;
/* Test C requires that the ratio of the increase in delay from the minimum
one in the stats data register to the standard deviation of the offsets
@@ -1707,7 +1741,7 @@ NCR_TakeSourceOffline(NCR_Instance inst)
void
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
{
if (new_minpoll < MIN_POLL)
if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL)
return;
inst->minpoll = new_minpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
@@ -1720,7 +1754,7 @@ NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
void
NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
{
if (new_maxpoll < MIN_POLL)
if (new_maxpoll < MIN_POLL || new_maxpoll > MAX_POLL)
return;
inst->maxpoll = new_maxpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2014
* Copyright (C) Miroslav Lichvar 2009, 2013-2015
*
* 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
@@ -97,8 +97,13 @@ prepare_socket(int family, int port_number, int client_only)
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
if (!client_only) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
} else {
DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
}
return INVALID_SOCK_FD;
}

View File

@@ -226,7 +226,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->ref_id = params->ref_id;
else {
unsigned char ref[5] = { 0, 0, 0, 0, 0 };
unsigned int index = ARR_GetSize(refclocks);
unsigned int index = ARR_GetSize(refclocks) - 1;
snprintf((char *)ref, sizeof (ref), "%3.3s", params->driver_name);
ref[3] = index % 10 + '0';
@@ -367,7 +367,9 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time);
dispersion += instance->precision;
if (!valid_sample_time(instance, sample_time))
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time))
return 0;
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
@@ -407,7 +409,8 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
dispersion += instance->precision;
if (!valid_sample_time(instance, pulse_time))
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
!valid_sample_time(instance, pulse_time))
return 0;
rate = instance->pps_rate;

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2014
* Copyright (C) Miroslav Lichvar 2009-2015
*
* 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
@@ -50,7 +50,7 @@ static int our_leap_sec;
static int our_stratum;
static uint32_t our_ref_id;
static IPAddr our_ref_ip;
struct timeval our_ref_time; /* Stored relative to reference, NOT local time */
struct timeval our_ref_time;
static double our_skew;
static double our_residual_freq;
static double our_root_delay;
@@ -98,6 +98,17 @@ static double drift_file_age;
static void update_drift_file(double, double);
/* Leap second handling mode */
static REF_LeapMode leap_mode;
/* Flag indicating the clock was recently corrected for leap second and it may
not have correct time yet (missing 23:59:60 in the UTC time scale) */
static int leap_in_progress;
/* Timer for the leap second handler */
static int leap_timer_running;
static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname;
static time_t last_tz_leap_check;
@@ -136,6 +147,7 @@ static double last_ref_update_interval;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */
@@ -148,6 +160,9 @@ handle_slew(struct timeval *raw,
void *anything)
{
double delta;
struct timeval now;
UTI_AdjustTimeval(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
last_ref_update.tv_sec = 0;
@@ -155,6 +170,13 @@ handle_slew(struct timeval *raw,
} else if (last_ref_update.tv_sec) {
UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
}
/* When the clock was stepped, check if that doesn't change our leap status
and also reset the leap timeout to undo the shift in the scheduler */
if (change_type != LCL_ChangeAdjust && our_leap_sec && !leap_in_progress) {
LCL_ReadRawTime(&now);
update_leap_status(our_leap_status, now.tv_sec, 1);
}
}
/* ================================================== */
@@ -217,11 +239,18 @@ REF_Initialise(void)
enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
leap_timer_running = 0;
leap_in_progress = 0;
leap_mode = CNF_GetLeapSecMode();
/* Switch to step mode if the system driver doesn't support leap */
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
leap_mode = REF_LeapModeStep;
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2008 and Dec 31 2008 */
if (get_tz_leap(1214784000) == LEAP_Normal &&
get_tz_leap(1230681600) == LEAP_InsertSecond) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
if (get_tz_leap(1341014400) == LEAP_InsertSecond &&
get_tz_leap(1356912000) == LEAP_Normal) {
LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname);
} else {
LOG(LOGS_WARN, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname);
@@ -263,9 +292,7 @@ REF_Initialise(void)
void
REF_Finalise(void)
{
if (our_leap_sec) {
LCL_SetLeap(0);
}
update_leap_status(LEAP_Unsynchronised, 0, 0);
if (drift_file) {
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
@@ -301,6 +328,14 @@ REF_SetModeEndHandler(REF_ModeEndHandler handler)
/* ================================================== */
REF_LeapMode
REF_GetLeapMode(void)
{
return leap_mode;
}
/* ================================================== */
static double
Sqr(double x)
{
@@ -403,14 +438,10 @@ update_fb_drifts(double freq_ppm, double update_interval)
fb_drift_timeout_id = -1;
}
if (update_interval < 0.0 || update_interval > last_ref_update_interval * 4.0)
if (update_interval < 1.0 || update_interval > last_ref_update_interval * 4.0)
return;
for (i = 0; i < fb_drift_max - fb_drift_min + 1; i++) {
/* Don't allow differences larger than 10 ppm */
if (fabs(freq_ppm - fb_drifts[i].freq) > 10.0)
fb_drifts[i].secs = 0.0;
secs = 1 << (i + fb_drift_min);
if (fb_drifts[i].secs < secs) {
/* Calculate average over 2 * secs interval before switching to
@@ -436,11 +467,12 @@ update_fb_drifts(double freq_ppm, double update_interval)
static void
fb_drift_timeout(void *arg)
{
assert(are_we_synchronised == 0);
assert(next_fb_drift >= fb_drift_min && next_fb_drift <= fb_drift_max);
fb_drift_timeout_id = -1;
DEBUG_LOG(LOGF_Reference, "Fallback drift %d active: %f ppm",
next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq);
LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq);
REF_SetUnsynchronised();
}
@@ -657,7 +689,89 @@ get_tz_leap(time_t when)
/* ================================================== */
static void
update_leap_status(NTP_Leap leap, time_t now)
leap_end_timeout(void *arg)
{
leap_timer_running = 0;
leap_in_progress = 0;
our_leap_sec = 0;
if (leap_mode == REF_LeapModeSystem)
LCL_SetSystemLeap(0);
if (our_leap_status == LEAP_InsertSecond ||
our_leap_status == LEAP_DeleteSecond)
our_leap_status = LEAP_Normal;
}
/* ================================================== */
static void
leap_start_timeout(void *arg)
{
leap_in_progress = 1;
switch (leap_mode) {
case REF_LeapModeSystem:
DEBUG_LOG(LOGF_Reference, "Waiting for system clock leap second correction");
break;
case REF_LeapModeSlew:
LCL_NotifyLeap(our_leap_sec);
LCL_AccumulateOffset(our_leap_sec, 0.0);
LOG(LOGS_WARN, LOGF_Reference, "Adjusting system clock for leap second");
break;
case REF_LeapModeStep:
LCL_NotifyLeap(our_leap_sec);
LCL_ApplyStepOffset(our_leap_sec);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped for leap second");
break;
case REF_LeapModeIgnore:
LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second");
break;
default:
break;
}
/* Wait until the leap second is over with some extra room to be safe */
leap_timeout_id = SCH_AddTimeoutByDelay(2.0, leap_end_timeout, NULL);
}
/* ================================================== */
static void
set_leap_timeout(time_t now)
{
struct timeval when;
/* Stop old timer if there is one */
if (leap_timer_running) {
SCH_RemoveTimeout(leap_timeout_id);
leap_timer_running = 0;
leap_in_progress = 0;
}
if (!our_leap_sec)
return;
/* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock
will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_usec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
if (leap_mode == REF_LeapModeSystem) {
when.tv_sec--;
when.tv_usec = 500000;
}
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
leap_timer_running = 1;
}
/* ================================================== */
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{
int leap_sec;
@@ -680,9 +794,22 @@ update_leap_status(NTP_Leap leap, time_t now)
}
}
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
LCL_SetLeap(leap_sec);
if (reset || (leap_sec != our_leap_sec && !REF_IsLeapSecondClose())) {
our_leap_sec = leap_sec;
switch (leap_mode) {
case REF_LeapModeSystem:
LCL_SetSystemLeap(our_leap_sec);
/* Fall through */
case REF_LeapModeSlew:
case REF_LeapModeStep:
case REF_LeapModeIgnore:
set_leap_timeout(now);
break;
default:
assert(0);
break;
}
}
our_leap_status = leap;
@@ -920,12 +1047,12 @@ REF_SetReference(int stratum,
our_residual_freq = frequency;
}
update_leap_status(leap, raw_now.tv_sec);
update_leap_status(leap, raw_now.tv_sec, 0);
maybe_log_offset(our_offset, raw_now.tv_sec);
if (step_offset != 0.0) {
LCL_ApplyStepOffset(step_offset);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
if (LCL_ApplyStepOffset(step_offset))
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
}
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
@@ -955,6 +1082,7 @@ REF_SetReference(int stratum,
/* Update fallback drifts */
if (fb_drifts) {
update_fb_drifts(abs_freq_ppm, update_interval);
schedule_fb_drift(&now);
}
last_ref_update_interval = update_interval;
@@ -1015,7 +1143,7 @@ REF_SetUnsynchronised(void)
schedule_fb_drift(&now);
}
update_leap_status(LEAP_Unsynchronised, 0);
update_leap_status(LEAP_Unsynchronised, 0, 0);
are_we_synchronised = 0;
LCL_SetSyncStatus(0, 0.0, 0.0);
@@ -1061,7 +1189,7 @@ REF_GetReferenceParams
UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
extra_dispersion = (our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
*leap_status = our_leap_status;
*leap_status = !leap_in_progress ? our_leap_status : LEAP_Unsynchronised;
*ref_id = our_ref_id;
*ref_time = our_ref_time;
*root_delay = our_root_delay;
@@ -1121,6 +1249,14 @@ REF_GetOurStratum(void)
/* ================================================== */
double
REF_GetSkew(void)
{
return our_skew;
}
/* ================================================== */
void
REF_ModifyMaxupdateskew(double new_max_update_skew)
{

View File

@@ -35,6 +35,14 @@
#include "ntp.h"
#include "reports.h"
/* Leap second handling modes */
typedef enum {
REF_LeapModeSystem,
REF_LeapModeSlew,
REF_LeapModeStep,
REF_LeapModeIgnore,
} REF_LeapMode;
/* Init function */
extern void REF_Initialise(void);
@@ -61,6 +69,9 @@ typedef void (*REF_ModeEndHandler)(int result);
/* Set the handler for being notified of mode ending */
extern void REF_SetModeEndHandler(REF_ModeEndHandler handler);
/* Get leap second handling mode */
extern REF_LeapMode REF_GetLeapMode(void);
/* Function which takes a local cooked time and returns the estimated
time of the reference. It also returns the other parameters
required for forming the outgoing NTP packet.
@@ -154,6 +165,9 @@ REF_SetUnsynchronised(void);
synchronised */
extern int REF_GetOurStratum(void);
/* Return the current skew */
extern double REF_GetSkew(void);
/* Modify the setting for the maximum skew we are prepared to allow updates on (in ppm). */
extern void REF_ModifyMaxupdateskew(double new_max_update_skew);

6
rtc.c
View File

@@ -93,9 +93,9 @@ fallback_time_init(void)
LCL_ReadCookedTime(&now, NULL);
if (now.tv_sec < buf.st_mtime) {
LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime);
LOG(LOGS_INFO, LOGF_Rtc,
"System clock set from driftfile %s", drift_file);
if (LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime))
LOG(LOGS_INFO, LOGF_Rtc, "System clock set from driftfile %s",
drift_file);
}
}

View File

@@ -1043,9 +1043,9 @@ RTC_Linux_TimePreInit(void)
/* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) {
LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
accumulated_error);
LCL_ApplyStepOffset(sys_offset);
if (LCL_ApplyStepOffset(sys_offset))
LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
accumulated_error);
}
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970");

267
smooth.c Normal file
View File

@@ -0,0 +1,267 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2015
*
* 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.
*
**********************************************************************
=======================================================================
Routines implementing time smoothing.
*/
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "reference.h"
#include "smooth.h"
#include "util.h"
/*
Time smoothing determines an offset that needs to be applied to the cooked
time to make it smooth for external observers. Observed offset and frequency
change slowly and there are no discontinuities. This can be used on an NTP
server to make it easier for the clients to track the time and keep their
clocks close together even when large offset or frequency corrections are
applied to the server's clock (e.g. after being offline for longer time).
Accumulated offset and frequency are smoothed out in three stages. In the
first stage, the frequency is changed at a constant rate (wander) up to a
maximum, in the second stage the frequency stays at the maximum for as long
as needed and in the third stage the frequency is brought back to zero.
|
max_freq +-------/--------\-------------
| /| |\
freq | / | | \
| / | | \
| / | | \
0 +--/----+--------+----\--------
| / | | | time
|/ | | |
stage 1 2 3
Integral of this function is the smoothed out offset. It's a continuous
piecewise polynomial with two quadratic parts and one linear.
*/
struct stage {
double wander;
double length;
};
#define NUM_STAGES 3
static struct stage stages[NUM_STAGES];
/* Enabled/disabled smoothing */
static int enabled;
/* Maximum skew/max_wander ratio to start updating offset and frequency */
#define UNLOCK_SKEW_WANDER_RATIO 10000
static int locked;
/* Maximum wander and frequency offset */
static double max_wander;
static double max_freq;
/* Frequency offset, time offset and the time of the last smoothing update */
static double smooth_freq;
static double smooth_offset;
static struct timeval last_update;
static void
get_offset_freq(struct timeval *now, double *offset, double *freq)
{
double elapsed, length;
int i;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
*offset = smooth_offset;
*freq = smooth_freq;
for (i = 0; i < NUM_STAGES; i++) {
if (elapsed <= 0.0)
break;
length = stages[i].length;
if (length >= elapsed)
length = elapsed;
*offset -= length * (2.0 * *freq + stages[i].wander * length) / 2.0;
*freq += stages[i].wander * length;
elapsed -= length;
}
if (elapsed > 0.0)
*offset -= elapsed * *freq;
}
static void
update_stages(void)
{
double s1, s2, s, l1, l2, l3, lc, f, f2;
int i, dir;
/* Prepare the three stages so that the integral of the frequency offset
is equal to the offset that should be smoothed out */
s1 = smooth_offset / max_wander;
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
l1 = l2 = l3 = 0.0;
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. If length of the 1st stage comes out negative, switch
its direction. */
for (dir = -1; dir <= 1; dir += 2) {
s = dir * s1 + s2;
if (s >= 0.0) {
l3 = sqrt(s);
l1 = l3 - dir * smooth_freq / max_wander;
if (l1 >= 0.0)
break;
}
}
assert(dir <= 1 && l1 >= 0.0 && l3 >= 0.0);
/* If the limit was reached, shorten 1st+3rd stages and set a 2nd stage */
f = dir * smooth_freq + l1 * max_wander - max_freq;
if (f > 0.0) {
lc = f / max_wander;
/* No 1st stage if the frequency is already above the maximum */
if (lc > l1) {
lc = l1;
f2 = dir * smooth_freq;
} else {
f2 = max_freq;
}
l2 = lc * (2.0 + f / f2);
l1 -= lc;
l3 -= lc;
}
stages[0].wander = dir * max_wander;
stages[0].length = l1;
stages[1].wander = 0.0;
stages[1].length = l2;
stages[2].wander = -dir * max_wander;
stages[2].length = l3;
for (i = 0; i < NUM_STAGES; i++) {
DEBUG_LOG(LOGF_Smooth, "Smooth stage %d wander %e length %f",
i + 1, stages[i].wander, stages[i].length);
}
}
static void
update_smoothing(struct timeval *now, double offset, double freq)
{
/* Don't accept offset/frequency until the clock has stabilized */
if (locked) {
if (REF_GetSkew() / max_wander < UNLOCK_SKEW_WANDER_RATIO) {
LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated");
locked = 0;
}
return;
}
get_offset_freq(now, &smooth_offset, &smooth_freq);
smooth_offset += offset;
smooth_freq = (smooth_freq - freq) / (1.0 - freq);
last_update = *now;
update_stages();
DEBUG_LOG(LOGF_Smooth, "Smooth offset %e freq %e", smooth_offset, smooth_freq);
}
static void
handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
double delta;
if (change_type == LCL_ChangeAdjust)
update_smoothing(cooked, doffset, dfreq);
UTI_AdjustTimeval(&last_update, cooked, &last_update, &delta, dfreq, doffset);
}
void SMT_Initialise(void)
{
CNF_GetSmooth(&max_freq, &max_wander);
if (max_freq <= 0.0 || max_wander <= 0.0) {
enabled = 0;
return;
}
enabled = 1;
locked = 1;
/* Convert from ppm */
max_freq *= 1e-6;
max_wander *= 1e-6;
LCL_AddParameterChangeHandler(handle_slew, NULL);
}
void SMT_Finalise(void)
{
}
int SMT_IsEnabled(void)
{
return enabled;
}
double
SMT_GetOffset(struct timeval *now)
{
double offset, freq;
if (!enabled)
return 0.0;
get_offset_freq(now, &offset, &freq);
return offset;
}
void
SMT_Reset(struct timeval *now)
{
if (!enabled)
return;
locked = 1;
smooth_offset = 0.0;
smooth_freq = 0.0;
last_update = *now;
}

40
smooth.h Normal file
View File

@@ -0,0 +1,40 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2015
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
This module implements time smoothing.
*/
#ifndef GOT_SMOOTH_H
#define GOT_SMOOTH_H
extern void SMT_Initialise(void);
extern void SMT_Finalise(void);
extern int SMT_IsEnabled(void);
extern double SMT_GetOffset(struct timeval *now);
extern void SMT_Reset(struct timeval *now);
#endif

View File

@@ -513,7 +513,7 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
{
struct timeval src_ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion, elapsed;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew;
int i, index, combined;
@@ -524,6 +524,10 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
sum_offset_weight = sum_offset = sum2_offset_sd = 0.0;
sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0;
sel_src_distance = sources[selected_source_index]->sel_info.root_distance;
if (sources[selected_source_index]->type == SRC_NTP)
sel_src_distance += reselect_distance;
for (i = combined = 0; i < n_sel_sources; i++) {
index = sel_sources[i];
SST_GetTrackingData(sources[index]->stats, &src_ref_time,
@@ -536,8 +540,7 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
are not close, or it was recently marked as distant */
if (index != selected_source_index &&
(sources[index]->sel_info.root_distance > combine_limit *
(reselect_distance + sources[selected_source_index]->sel_info.root_distance) ||
(sources[index]->sel_info.root_distance > combine_limit * sel_src_distance ||
fabs(*frequency - src_frequency) >
combine_limit * (*skew + src_skew + LCL_GetMaxClockError()))) {
/* Use a smaller penalty in first few updates */

View File

@@ -634,14 +634,14 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
inst->offsets[i] += delta_time;
}
/* Do a half-baked update to the regression estimates */
/* Update the regression estimates */
prev = inst->offset_time;
prev_offset = inst->estimated_offset;
prev_freq = inst->estimated_frequency;
UTI_AdjustTimeval(&(inst->offset_time), when, &(inst->offset_time),
&delta_time, dfreq, doffset);
inst->estimated_offset += delta_time;
inst->estimated_frequency -= 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",
inst->n_samples, inst->runs_samples,

View File

@@ -53,7 +53,7 @@ typedef struct {
#define SRC_DEFAULT_MINPOLL 6
#define SRC_DEFAULT_MAXPOLL 10
#define SRC_DEFAULT_PRESEND_MINPOLL 0
#define SRC_DEFAULT_MAXDELAY 16.0
#define SRC_DEFAULT_MAXDELAY 3.0
#define SRC_DEFAULT_MAXDELAYRATIO 0.0
#define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0
#define SRC_DEFAULT_MINSTRATUM 0

View File

@@ -107,6 +107,18 @@ handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
}
}
/* ================================================== */
static double
clamp_freq(double freq)
{
if (freq > max_freq)
return max_freq;
if (freq < -max_freq)
return -max_freq;
return freq;
}
/* ================================================== */
/* End currently running slew and start a new one */
@@ -144,11 +156,7 @@ update_slew(void)
corr_freq = max_corr_freq;
/* Get the new real frequency and clamp it */
total_freq = base_freq + corr_freq * (1.0e6 - base_freq);
if (total_freq > max_freq)
total_freq = max_freq;
else if (total_freq < -max_freq)
total_freq = -max_freq;
total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq));
/* Set the new frequency (the actual frequency returned by the call may be
slightly different from the requested frequency due to rounding) */
@@ -250,7 +258,7 @@ offset_convert(struct timeval *raw,
/* ================================================== */
/* Positive means currently fast of true time, i.e. jump backwards */
static void
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time;
@@ -260,13 +268,16 @@ apply_step_offset(double offset)
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
LOG_FATAL(LOGF_SysGeneric, "settimeofday() failed");
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed");
return 0;
}
LCL_ReadRawTime(&old_time);
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
lcl_InvokeDispersionNotifyHandlers(fabs(err));
return 1;
}
/* ================================================== */
@@ -327,7 +338,7 @@ SYS_Generic_Finalise(void)
slew_timer_running = 0;
}
(*drv_set_freq)(base_freq);
(*drv_set_freq)(clamp_freq(base_freq));
}
/* ================================================== */

View File

@@ -100,12 +100,15 @@ our_round(double x)
/* ================================================== */
/* Positive means currently fast of true time, i.e. jump backwards */
static void
static int
apply_step_offset(double offset)
{
if (TMX_ApplyStepOffset(-offset) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
DEBUG_LOG(LOGF_SysLinux, "adjtimex() failed");
return 0;
}
return 1;
}
/* ================================================== */
@@ -174,6 +177,15 @@ read_frequency(void)
static void
set_leap(int leap)
{
int current_leap;
if (TMX_GetLeap(&current_leap) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
}
if (current_leap == leap)
return;
if (TMX_SetLeap(leap) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
}

View File

@@ -212,7 +212,7 @@ accrue_offset(double offset, double corr_rate)
/* Positive offset means system clock is fast of true time, therefore
step backwards */
static void
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, T1;
@@ -226,7 +226,8 @@ apply_step_offset(double offset)
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "settimeofday() failed");
DEBUG_LOG(LOGF_SysNetBSD, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, offset, &T1);
@@ -234,6 +235,7 @@ apply_step_offset(double offset)
start_adjust();
return 1;
}
/* ================================================== */

View File

@@ -219,7 +219,7 @@ accrue_offset(double offset, double corr_rate)
/* Positive offset means system clock is fast of true time, therefore
step backwards */
static void
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, rounded_new_time, T1;
@@ -248,7 +248,8 @@ apply_step_offset(double offset)
UTI_DiffTimevalsToDouble(&rounding_error, &rounded_new_time, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
LOG_FATAL(LOGF_SysSolaris, "settimeofday() failed");
DEBUG_LOG(LOGF_SysSolaris, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, offset, &T1);
@@ -257,6 +258,8 @@ apply_step_offset(double offset)
offset_register += rounding_error;
start_adjust();
return 1;
}
/* ================================================== */

View File

@@ -223,7 +223,7 @@ accrue_offset(double offset, double corr_rate)
/* Positive offset means system clock is fast of true time, therefore
step backwards */
static void
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, T1;
@@ -236,7 +236,8 @@ apply_step_offset(double offset)
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
LOG_FATAL(LOGF_SysSunOS, "settimeofday() failed");
DEBUG_LOG(LOGF_SysSunOS, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, offset, &T1);
@@ -244,6 +245,7 @@ apply_step_offset(double offset)
start_adjust();
return 1;
}
/* ================================================== */

View File

@@ -6,6 +6,7 @@ test_start "source selection"
# Falsetickers should be detected if their number is less than half of all
base_delay=1e-3
servers=5
for falsetickers in 1 2; do
@@ -29,7 +30,7 @@ done
servers=3
falsetickers=0
base_delay="(+ $default_base_delay (equal 0.1 to 2) (equal 0.1 to 3))"
base_delay="(+ 1e-3 (equal 0.1 to 2) (equal 0.1 to 3))"
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -5,12 +5,35 @@ test_start "leap second"
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
leap=$[2 * 24 * 3600]
limit=$[4 * 24 * 3600]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
server_step="(* 1.0 (equal 0.1 (sum 1.0) $[2 * 24 * 3600 + 1]))"
client_step="(* 1.0 (equal 0.1 (sum 1.0) $[2 * 24 * 3600 + 1]))"
refclock_jitter=1e-9
refclock_offset="(* -1.0 (equal 0.1 (max (sum 1.0) $leap) $leap))"
for leapmode in system step slew; do
client_conf="leapsecmode $leapmode"
if [ $leapmode = slew ]; then
max_sync_time=$[$leap + 12]
else
max_sync_time=$[$leap]
fi
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC
leapsecmode slew
smoothtime 400 0.001"
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -5,7 +5,7 @@ test_start "presend option"
min_sync_time=140
max_sync_time=260
client_server_options="presend 6"
client_server_options="presend 6 maxdelay 16"
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -0,0 +1,23 @@
#!/bin/bash
. test.common
test_start "fallback drift"
limit=100000
wander=0.0
jitter=1e-6
time_offset=10
freq_offset="(* 1e-4 (sine 1000))"
base_delay="(* -1.0 (equal 0.1 (min time 4250) 4250))"
client_server_options="minpoll 4 maxpoll 4"
client_conf="fallbackdrift 6 10"
time_max_limit=1e0
time_rms_limit=1e0
freq_max_limit=5e-4
freq_rms_limit=5e-4
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
test_pass

28
test/simulation/118-maxdelay Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
. test.common
test_start "maxdelay options"
max_sync_time=2000
base_delay=1e-5
jitter=1e-5
wander=0.0
freq_offset="(sum 1e-10)"
time_rms_limit=2e-4
client_server_options="maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevratio 2.0"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
for client_server_options in "maxdelay 2e-5" \ "maxdelayratio 1.001"; do
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync && test_fail
done
test_pass

68
test/simulation/119-smoothtime Executable file
View File

@@ -0,0 +1,68 @@
#!/bin/bash
. test.common
test_start "smoothtime option"
server_strata=2
server_conf="smoothtime 400 0.001"
min_sync_time=250
max_sync_time=1000
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
limit=10000
refclock_jitter=1e-4
refclock_offset="(* 10.0 (equal 0.1 (max (sum 1.0) 1000) 1000))"
server_step="(* -10.0 (equal 0.1 (sum 1.0) 1))"
server_strata=1
server_conf="refclock SHM 0 dpoll 4 poll 6
smoothtime 2000 1"
time_offset=-10
client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100"
min_sync_time=8000
max_sync_time=8500
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
min_sync_time=$default_min_sync_time
max_sync_time=$default_max_sync_time
time_max_limit=11
time_rms_limit=11
freq_max_limit=1e-2
freq_rms_limit=2e-3
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
refclock_jitter=1e-9
refclock_offset="(* 1e-1 (triangle 1000) (+ -1.0 (pulse 1000 10000)))"
server_step=""
server_conf="refclock SHM 0 dpoll 4 poll 6 minsamples 4 maxsamples 4
smoothtime 1e4 1e-6"
client_server_options="minpoll 4 maxpoll 4"
time_offset=0.1
jitter=1e-6
wander=0.0
min_sync_time=30
max_sync_time=40
time_max_limit=1e-5
time_rms_limit=5e-6
freq_max_limit=1e-6
freq_rms_limit=1e-7
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
test_pass

View File

@@ -18,7 +18,7 @@ export PATH=../../:$PATH
export CLKNETSIM_PATH=clknetsim
# Known working clknetsim revision
clknetsim_revision=565e593b4403d035b459b4f8516dacaeaeeb7739
clknetsim_revision=3eb3a8d9acf60c31f5acc66617175fc748ef367e
clknetsim_url=https://github.com/mlichvar/clknetsim/archive/$clknetsim_revision.tar.gz
# Only Linux is supported
@@ -49,6 +49,7 @@ default_base_delay=1e-4
default_jitter=1e-4
default_wander=1e-9
default_refclock_jitter=""
default_refclock_offset=0.0
default_update_interval=0
default_shift_pll=2
@@ -168,7 +169,7 @@ get_delay_expr() {
}
get_refclock_expr() {
echo "(* $refclock_jitter (normal))"
echo "(+ $refclock_offset (* $refclock_jitter (normal)))"
}
get_chronyd_nodes() {
@@ -261,7 +262,7 @@ check_source_selection() {
for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
! grep -q 'no majority\|no reachable sources' tmp/log.$i && \
! grep -q 'no majority\|no selectable sources' tmp/log.$i && \
grep -q 'Selected source' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1

39
util.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2012-2013
* Copyright (C) Miroslav Lichvar 2009, 2012-2014
*
* 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
@@ -606,6 +606,43 @@ UTI_Int64ToTimeval(NTP_int64 *src,
/* ================================================== */
/* Maximum offset between two sane times */
#define MAX_OFFSET 4294967296.0
/* Minimum allowed distance from maximum 32-bit time_t */
#define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600)
int
UTI_IsTimeOffsetSane(struct timeval *tv, double offset)
{
double t;
/* Handle nan correctly here */
if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET))
return 0;
UTI_TimevalToDouble(tv, &t);
t += offset;
/* Time before 1970 is not considered valid */
if (t < 0.0)
return 0;
#ifdef HAVE_LONG_TIME_T
/* Check if it's in the interval to which NTP time is mapped */
if (t < (double)NTP_ERA_SPLIT || t > (double)(NTP_ERA_SPLIT + (1LL << 32)))
return 0;
#else
/* Don't get too close to 32-bit time_t overflow */
if (t > (double)(0x7fffffff - MIN_ENDOFTIME_DISTANCE))
return 0;
#endif
return 1;
}
/* ================================================== */
void
UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest)
{

3
util.h
View File

@@ -104,6 +104,9 @@ extern void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest, uint32_t fu
extern void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest);
/* Check if time + offset is sane */
extern int UTI_IsTimeOffsetSane(struct timeval *tv, double offset);
extern void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest);
extern void UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest);

View File

@@ -71,7 +71,7 @@ TMX_ResetOffset(void)
/* Set status back */
txc.modes = ADJ_STATUS;
txc.modes = status;
txc.status = status;
if (adjtimex(&txc) < 0)
return -1;
@@ -123,6 +123,28 @@ TMX_SetLeap(int leap)
return adjtimex(&txc);
}
int
TMX_GetLeap(int *leap)
{
struct timex txc;
txc.modes = 0;
if (adjtimex(&txc) < 0)
return -1;
status &= ~(STA_INS | STA_DEL);
status |= txc.status & (STA_INS | STA_DEL);
if (status & STA_INS)
*leap = 1;
else if (status & STA_DEL)
*leap = -1;
else
*leap = 0;
return 0;
}
int TMX_SetSync(int sync, double est_error, double max_error)
{
struct timex txc;

View File

@@ -31,6 +31,7 @@ int TMX_ResetOffset(void);
int TMX_SetFrequency(double *freq, long tick);
int TMX_GetFrequency(double *freq, long *tick);
int TMX_SetLeap(int leap);
int TMX_GetLeap(int *leap);
int TMX_SetSync(int sync, double est_error, double max_error);
int TMX_TestStepOffset(void);
int TMX_ApplyStepOffset(double offset);