diff --git a/chrony.texi b/chrony.texi index f45cdde..9b75eff 100644 --- a/chrony.texi +++ b/chrony.texi @@ -1192,6 +1192,7 @@ directives can occur in any order in the file. * logdir directive:: Specify directory for logging * mailonchange directive:: Send email if a clock correction above a threshold occurs * makestep directive:: Step system clock if large correction is needed +* maxchange directive:: Set maximum allowed offset * manual directive:: Allow manual entry using chronyc's settime cmd. * maxclockerror directive:: Set maximum frequency error of local clock * maxupdateskew directive:: Stop bad estimates upsetting machine clock @@ -2244,6 +2245,27 @@ makestep 1000 10 This would step system clock if the adjustment is larger than 1000 seconds, but only in the first ten clock updates. @c }}} +@c {{{ maxchange +@node maxchange directive +@subsection maxchange +This directive sets the maximum allowed offset corrected on a clock +update. The check is performed only after the specified number of +updates to allow a large initial adjustment of the system clock. When +an offset larger than the specified maximum occurs, it will be ignored +for the specified number of times and then @code{chronyd} will give up +and exit (a negative value can be used to never exit). In both cases +a message is sent to syslog. + +An example of the use of this directive is + +@example +maxchange 1000 1 2 +@end example + +After the first clock update, @code{chronyd} will check the offset on +every clock update, it will ignore two adjustments larger than 1000 +seconds and exit on another one. +@c }}} @c {{{ manual @node manual directive @subsection manual diff --git a/conf.c b/conf.c index c8df69e..bbd890c 100644 --- a/conf.c +++ b/conf.c @@ -98,6 +98,7 @@ static void parse_noclientlog(const char *); static void parse_clientloglimit(const char *); static void parse_fallbackdrift(const char *); static void parse_makestep(const char *); +static void parse_maxchange(const char *); static void parse_logchange(const char *); static void parse_mailonchange(const char *); static void parse_bindaddress(const char *); @@ -168,6 +169,12 @@ static int rtc_sync = 0; static int make_step_limit = 0; static double make_step_threshold = 0.0; +/* Number of updates before offset checking, number of ignored updates + before exiting and the maximum allowed offset */ +static int max_offset_delay = -1; +static int max_offset_ignore; +static double max_offset; + /* Flag set if we should log to syslog when a time adjustment exceeding the threshold is initiated */ static int do_log_change = 0; @@ -257,6 +264,7 @@ static const Command commands[] = { {"clientloglimit", 14, parse_clientloglimit}, {"fallbackdrift", 13, parse_fallbackdrift}, {"makestep", 8, parse_makestep}, + {"maxchange", 9, parse_maxchange}, {"logchange", 9, parse_logchange}, {"mailonchange", 12, parse_mailonchange}, {"bindaddress", 11, parse_bindaddress}, @@ -943,6 +951,19 @@ parse_makestep(const char *line) /* ================================================== */ +static void +parse_maxchange(const char *line) +{ + if (sscanf(line, "%lf %d %d", &max_offset, &max_offset_delay, &max_offset_ignore) != 3) { + max_offset_delay = -1; + LOG(LOGS_WARN, LOGF_Configure, + "Could not read offset, check delay or ignore limit for maximum change at line %d\n", + line_number); + } +} + +/* ================================================== */ + static void parse_logchange(const char *line) { @@ -1567,6 +1588,16 @@ CNF_GetMakeStep(int *limit, double *threshold) /* ================================================== */ +void +CNF_GetMaxChange(int *delay, int *ignore, double *offset) +{ + *delay = max_offset_delay; + *ignore = max_offset_ignore; + *offset = max_offset; +} + +/* ================================================== */ + void CNF_GetLogChange(int *enabled, double *threshold) { diff --git a/conf.h b/conf.h index 4c208c7..7f0f6b3 100644 --- a/conf.h +++ b/conf.h @@ -60,6 +60,7 @@ extern int CNF_GetCommandPort(void); extern int CNF_GetRTCOnUTC(void); extern int CNF_GetRTCSync(void); extern void CNF_GetMakeStep(int *limit, double *threshold); +extern void CNF_GetMaxChange(int *delay, int *ignore, double *offset); extern void CNF_GetLogChange(int *enabled, double *threshold); extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user); extern int CNF_GetNoClientLog(void); diff --git a/reference.c b/reference.c index 7c8f5d0..7bf8ec7 100644 --- a/reference.c +++ b/reference.c @@ -68,6 +68,12 @@ static int initialised = 0; static int make_step_limit; static double make_step_threshold; +/* Number of updates before offset checking, number of ignored updates + before exiting and the maximum allowed offset */ +static int max_offset_delay; +static int max_offset_ignore; +static double max_offset; + /* Flag and threshold for logging clock changes to syslog */ static int do_log_change; static double log_change_threshold; @@ -191,6 +197,7 @@ REF_Initialise(void) enable_local_stratum = CNF_AllowLocalReference(&local_stratum); CNF_GetMakeStep(&make_step_limit, &make_step_threshold); + CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset); CNF_GetLogChange(&do_log_change, &log_change_threshold); CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user); @@ -492,6 +499,33 @@ maybe_make_step() /* ================================================== */ +static int +is_offset_ok(double offset) +{ + if (max_offset_delay < 0) + return 1; + + if (max_offset_delay > 0) { + max_offset_delay--; + return 1; + } + + offset = fabs(offset); + if (offset > max_offset) { + LOG(LOGS_WARN, LOGF_Reference, + "Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ", + offset, max_offset, !max_offset_ignore ? "exiting" : "ignored"); + if (!max_offset_ignore) + SCH_QuitProgram(); + else if (max_offset_ignore > 0) + max_offset_ignore--; + return 0; + } + return 1; +} + +/* ================================================== */ + static void update_leap_status(NTP_Leap leap) { @@ -594,6 +628,12 @@ REF_SetReference(int stratum, } } + LCL_ReadCookedTime(&now, NULL); + UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time); + our_offset = offset + elapsed * frequency; + + if (!is_offset_ok(offset)) + return; are_we_synchronised = 1; our_stratum = stratum + 1; @@ -606,10 +646,6 @@ REF_SetReference(int stratum, our_root_delay = root_delay; our_root_dispersion = root_dispersion; - LCL_ReadCookedTime(&now, NULL); - UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time); - our_offset = offset + elapsed * frequency; - update_leap_status(leap); if (last_ref_update.tv_sec) {