Float distance;
int32_t orphan;
Float activate;
- uint32_t reserved[2];
+ Float wait_synced;
+ Float wait_unsynced;
int32_t EOR;
} REQ_Local;
static int
process_cmd_local(CMD_Request *msg, char *line)
{
+ double distance = 0.0, activate = 0.0, wait_synced = 0.0, wait_unsynced = 0.0;
int on_off, stratum = 0, orphan = 0;
- double distance = 0.0, activate = 0.0;
if (!strcmp(line, "off")) {
on_off = 0;
- } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate)) {
+ } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate,
+ &wait_synced, &wait_unsynced)) {
on_off = 1;
} else {
LOG(LOGS_ERR, "Invalid syntax for local command");
msg->data.local.distance = UTI_FloatHostToNetwork(distance);
msg->data.local.orphan = htonl(orphan);
msg->data.local.activate = UTI_FloatHostToNetwork(activate);
- memset(msg->data.local.reserved, 0, sizeof (msg->data.local.reserved));
+ msg->data.local.wait_synced = UTI_FloatHostToNetwork(wait_synced);
+ msg->data.local.wait_unsynced = UTI_FloatHostToNetwork(wait_unsynced);
return 1;
}
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
ntohl(rx_message->data.local.orphan),
- UTI_FloatNetworkToHost(rx_message->data.local.activate));
+ UTI_FloatNetworkToHost(rx_message->data.local.activate),
+ UTI_FloatNetworkToHost(rx_message->data.local.wait_synced),
+ UTI_FloatNetworkToHost(rx_message->data.local.wait_unsynced));
} else {
REF_DisableLocal();
}
/* ================================================== */
int
-CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate)
+CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate,
+ double *wait_synced, double *wait_unsynced)
{
int n;
char *cmd;
*distance = 1.0;
*activate = 0.0;
*orphan = 0;
+ *wait_synced = 0;
+ *wait_unsynced = -1.0;
while (*line) {
cmd = line;
} else if (!strcasecmp(cmd, "activate")) {
if (sscanf(line, "%lf%n", activate, &n) != 1)
return 0;
+ } else if (!strcasecmp(cmd, "waitsynced")) {
+ if (sscanf(line, "%lf%n", wait_synced, &n) != 1)
+ return 0;
+ } else if (!strcasecmp(cmd, "waitunsynced")) {
+ if (sscanf(line, "%lf%n", wait_unsynced, &n) != 1)
+ return 0;
} else {
return 0;
}
line += n;
}
+ if (*wait_unsynced < 0.0)
+ *wait_unsynced = *orphan ? 300 : 0.0;
+
return 1;
}
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
/* Parse a command to enable local reference */
-extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate);
+extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance,
+ double *activate, double *wait_synced, double *wait_unsynced);
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);
static int local_orphan;
static double local_distance;
static double local_activate;
+static double local_wait_synced;
+static double local_wait_unsynced;
/* Threshold (in seconds) - if absolute value of initial error is less
than this, slew instead of stepping */
static void
parse_local(char *line)
{
- if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance, &local_activate))
+ if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance,
+ &local_activate, &local_wait_synced, &local_wait_unsynced))
command_parse_error();
enable_local = 1;
}
/* ================================================== */
int
-CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate)
+CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
+ double *wait_synced, double *wait_unsynced)
{
if (enable_local) {
*stratum = local_stratum;
*orphan = local_orphan;
*distance = local_distance;
*activate = local_activate;
+ *wait_synced = local_wait_synced;
+ *wait_unsynced = local_wait_unsynced;
return 1;
} else {
return 0;
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
-extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate);
+extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
+ double *wait_synced, double *wait_unsynced);
extern void CNF_SetupAccessRestrictions(void);
its time is fairly unreliable.
*distance* _distance_:::
This option sets the threshold for the root distance which will activate the local
-reference. If *chronyd* was synchronised to some source, the local reference
-will not be activated until its root distance reaches the specified value (the
-rate at which the distance is increasing depends on how well the clock was
-tracking the source). The default value is 1 second.
+reference. If *chronyd* was synchronised to a configured time source, the local
+reference will not be activated until its root distance reaches the specified
+value (the rate at which the distance is increasing depends on how well the
+clock was tracking the source). When the clock is not synchronised, it is
+considered to have an infinite root distance, i.e. the local reference
+activates as soon as allowed by the *waitunsynced* option. The default
+threshold is 1 second.
+
The current root distance can be calculated from root delay and root dispersion
(reported by the <<chronyc.adoc#tracking,*tracking*>> command in *chronyc*) as:
+
The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
*tos orphan* command).
+*waitsynced* _interval_:::
+This option specifies the minimum interval (in seconds) between the last update
+of the clock and activation of the local reference as configured by the
+*distance* and *activate* options. The *distance* option can be set to 0 to
+ignore the root distance and control the activation only by the interval. In
+such case it should be at least as long as the maximum expected polling
+interval to prevent frequent activation in normal polling of the source.
+The default minimum interval is 0.
+*waitunsynced* _interval_:::
+This option specifies how long (in seconds) *chronyd* needs to wait before
+activating the local reference when the clock is not considered to be
+synchronised (e.g. after start or the source selection failing due to no
+majority). This delay prevents *chronyd* from serving incorrect time to clients
+before the configured time sources are given a chance to synchronise the local
+clock. The default interval is 300 seconds if the *orphan* option is set,
+otherwise it is 0 (i.e. local reference activates immediately).
{blank}::
+
-An example of the directive is:
+Examples of the directive are:
+
----
+local stratum 5
local stratum 10 orphan distance 0.1 activate 0.5
+local stratum 10 orphan distance 0.0 waitsynced 7200 waitunsynced 300
----
[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
static double local_distance;
static int local_activate_ok;
static double local_activate;
+static double local_wait_synced;
+static double local_wait_unsynced;
static struct timespec local_ref_time;
static NTP_Leap our_leap_status;
static int our_leap_sec;
static uint32_t our_ref_id;
static IPAddr our_ref_ip;
static struct timespec our_ref_time;
+static double unsynchronised_since;
static double our_skew;
static double our_residual_freq;
static double our_root_delay;
correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
- &local_distance, &local_activate);
+ &local_distance, &local_activate,
+ &local_wait_synced,
+ &local_wait_unsynced);
UTI_ZeroTimespec(&local_ref_time);
+ unsynchronised_since = SCH_GetLastEventMonoTime();
leap_when = 0;
leap_timeout_id = 0;
our_ref_ip.family = IPADDR_INET4;
our_ref_ip.addr.in4 = 0;
our_stratum = 0;
+
+ if (are_we_synchronised)
+ unsynchronised_since = SCH_GetLastEventMonoTime();
are_we_synchronised = 0;
LCL_SetSyncStatus(0, 0.0, 0.0);
)
{
double dispersion, delta, distance;
+ int wait_local_ok;
assert(initialised);
if (are_we_synchronised) {
dispersion = get_root_dispersion(local_time);
+ wait_local_ok = UTI_DiffTimespecsToDouble(local_time, &our_ref_time) >= local_wait_synced;
} else {
dispersion = 0.0;
+ wait_local_ok = SCH_GetLastEventMonoTime() - unsynchronised_since >= local_wait_unsynced;
}
distance = our_root_delay / 2 + dispersion;
or the root distance exceeds the threshold */
if (are_we_synchronised &&
- !(enable_local_stratum && local_activate_ok && distance > local_distance)) {
+ !(enable_local_stratum && local_activate_ok && wait_local_ok &&
+ distance > local_distance)) {
*is_synchronised = 1;
*root_delay = our_root_delay;
*root_dispersion = dispersion;
- } else if (enable_local_stratum && local_activate_ok) {
+ } else if (enable_local_stratum && local_activate_ok && wait_local_ok) {
*is_synchronised = 0;
/* ================================================== */
void
-REF_EnableLocal(int stratum, double distance, int orphan, double activate)
+REF_EnableLocal(int stratum, double distance, int orphan, double activate,
+ double wait_synced, double wait_unsynced)
{
enable_local_stratum = 1;
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
local_activate = activate;
+ local_wait_synced = wait_synced;
+ local_wait_unsynced = wait_unsynced;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}
/* Modify makestep settings */
extern void REF_ModifyMakestep(int limit, double threshold);
-extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate);
+extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate,
+ double wait_synced, double wait_unsynced);
extern void REF_DisableLocal(void);
/* Check if either of the current raw and cooked time, and optionally a
"dfreq 1.0e-3" \
"doffset -1.0" \
"dump" \
- "local stratum 5 distance 1.0 activate 0.5 orphan" \
+ "local stratum 5 distance 1.0 activate 0.5 orphan waitsynced 100 waitunsynced 20" \
"local off" \
"makestep 10.0 3" \
"makestep" \
dump
dfreq 1.0e-3
doffset -0.01
-local stratum 5 distance 1.0 orphan
+local stratum 5 distance 1.0 orphan waitsynced 100 waitunsynced 10
local off
makestep 10.0 3
makestep
check_config_h 'FEAT_CMDMON 1' || test_skip
server_strata=3
-server_conf="local stratum 5 orphan
+server_conf="local stratum 5 orphan waitunsynced 0
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
+limit=1000
+server_conf="local stratum 5 orphan
+server 192.168.123.1 minpoll 6 maxpoll 6
+server 192.168.123.2 minpoll 6 maxpoll 6
+server 192.168.123.3 minpoll 6 maxpoll 6"
+server_server_options="minpoll 6 maxpoll 6"
+client_start=0
+client_server_conf="
+server 192.168.123.1 minpoll 6 maxpoll 6
+server 192.168.123.2 minpoll 6 maxpoll 6
+server 192.168.123.3 minpoll 6 maxpoll 6"
+client_conf="logdir tmp
+log measurements"
+chronyc_start=700
+chronyc_conf=""
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+check_file_messages "20.*123\.1.* 5 111 " 10 11 measurements.log || test_fail
+check_file_messages "20.*123\.1.* [6-9] 111 " 0 0 measurements.log || test_fail
+check_file_messages "20.*123\.2.* 5 111 " 2 4 measurements.log || test_fail
+check_file_messages "20.*123\.2.* 6 111 " 7 9 measurements.log || test_fail
+check_file_messages "20.*123\.2.* [7-9] 111 " 0 0 measurements.log || test_fail
+check_file_messages "20.*123\.3.* 5 111 " 2 4 measurements.log || test_fail
+check_file_messages "20.*123\.3.* 6 111 " 7 9 measurements.log || test_fail
+check_file_messages "20.*123\.3.* [7-9] 111 " 0 0 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+server_conf="local stratum 5 orphan distance 0.0 waitsynced 150 waitunsynced 0"
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* -1
+ (equal 0.1 from 1)
+ (equal 0.1 to 2)
+ (equal 0.1 (min time 500) 500)))
+EOF
+)
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+check_file_messages "20.*:1.:.*123\.1.* 5 111 " 6 6 measurements.log || test_fail
+check_file_messages "20.*:0.:.*123\.2.* 5 111 " 2 3 measurements.log || test_fail
+check_file_messages "20.*:1.:.*123\.2.* 5 111 " 6 7 measurements.log || test_fail
+check_file_messages "20.*:0.:.*123\.3.* 5 111 " 7 10 measurements.log || test_fail
+check_file_messages "20.*:1.:.*123\.3.* 5 111 " 0 1 measurements.log || test_fail
+rm -f tmp/measurements.log
+
limit=4000
wander=0.0
jitter=0.0
server_strata=1
server_conf=""
+server_server_options=""
+client_server_conf=""
client_server_options="minpoll 6 maxpoll 6 minsamples 64"
chronyc_start=1
chronyc_conf="timeout 1000000