Compare commits

...

41 Commits

Author SHA1 Message Date
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
41 changed files with 1108 additions and 114 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@

20
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
@@ -15,6 +17,7 @@ Enhancements
* Open NTP server port only when necessary (client access is allowed by
allow directive/command, peer or 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,20 @@ 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
* 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-1799)
* 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
===================

View File

@@ -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

@@ -1158,6 +1158,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 +1190,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 +1638,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 +1652,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 +1809,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
@@ -2873,7 +2930,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 +3038,42 @@ 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).
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

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,9 +1557,12 @@ 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();
if (!LCL_MakeStep()) {
tx_message->status = htons(STT_FAILED);
} else {
tx_message->status = htons(STT_SUCCESS);
}
}
/* ================================================== */

58
conf.c
View File

@@ -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);

89
local.c
View File

@@ -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 */

3
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();
@@ -495,6 +497,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

@@ -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. */
/* 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

@@ -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

@@ -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,6 +239,13 @@ 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 */
@@ -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,11 +1047,11 @@ 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);
if (LCL_ApplyStepOffset(step_offset))
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
}
@@ -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) {
if (LCL_ApplyStepOffset(sys_offset))
LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
accumulated_error);
LCL_ApplyStepOffset(sys_offset);
}
} 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

@@ -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

@@ -250,7 +250,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 +260,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;
}
/* ================================================== */

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

30
util.c
View File

@@ -606,6 +606,36 @@ UTI_Int64ToTimeval(NTP_int64 *src,
/* ================================================== */
/* Maximum offset between two sane times */
#define MAX_OFFSET 4294967296.0
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;
#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

@@ -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);