[ntpsec commit] Forward-port patches for NTP Classic [Bug 2855]: Conditional leap smearing.
Eric S. Raymond
esr at ntpsec.org
Thu Oct 22 18:04:16 UTC 2015
Module: ntpsec
Branch: master
Commit: 9144ed2193872433d11b74b8d96d78e4c77ca629
Changeset: http://git.ntpsec.org/ntpsec/commit/?id=9144ed2193872433d11b74b8d96d78e4c77ca629
Author: Eric S. Raymond <esr at thyrsus.com>
Date: Thu Oct 22 13:58:45 2015 -0400
Forward-port patches for NTP Classic [Bug 2855]: Conditional leap smearing.
This code rolls up all NTP Classic patches for leap smearing, also integrating
Martin Burnicki's documentation into the web tree.
---
NEWS | 2 +
docs/includes/misc-options.txt | 8 ++
docs/includes/special.txt | 1 +
docs/leap.txt | 2 +
docs/leapsmear.txt | 281 +++++++++++++++++++++++++++++++++++++++++
include/ntpd.h | 6 +
include/refidsmear.h | 3 +
libntp/refidsmear.c | 58 +++++++++
libntp/wscript | 1 +
ntpd/ntp_config.c | 7 +
ntpd/ntp_control.c | 19 ++-
ntpd/ntp_io.c | 8 ++
ntpd/ntp_leapsec.h | 16 +++
ntpd/ntp_parser.y | 10 ++
ntpd/ntp_proto.c | 93 +++++++++++++-
ntpd/ntp_timer.c | 81 ++++++++++++
16 files changed, 592 insertions(+), 4 deletions(-)
diff --git a/NEWS b/NEWS
index 3cd2af9..7ae3833 100644
--- a/NEWS
+++ b/NEWS
@@ -12,5 +12,7 @@ on user-visible changes.
robust, and require 2 consecutive timestamps to be consistent.
* [Bug 2845] Harden memory allocation in ntpd; implement and
use 'eallocarray(...)' where appropriate.
+* [Bug 2855] Implement conditional leap smear feature; includes
+ later fixes for parser support and reporting leap smear in the REFID.
// end
diff --git a/docs/includes/misc-options.txt b/docs/includes/misc-options.txt
index bed1686..b22cbd3 100644
--- a/docs/includes/misc-options.txt
+++ b/docs/includes/misc-options.txt
@@ -105,6 +105,14 @@ While not strictly a security function, the Autokey protocol provides
means to securely retrieve the current or updated leapsecond values
from a server.
++leapsmearinterval+ 'interval'::
+ This *experimental* option is only available if {ntpd} was built
+ with the +--enable-leap-smear+ option, It specifies the interval
+ over which a leap second correction will be applied. Recommended
+ values for this option are between 7200 (2 hours) and 86400 (24
+ hours). DO NOT USE THIS OPTION ON PUBLIC-ACCESS SERVERS! See
+ http://bugs.ntp.org/2855 for more information.
+
+logconfig+ _configkeyword_::
This command controls the amount and type of output written to the
system _syslog(3)_ facility or the alternate log file. By
diff --git a/docs/includes/special.txt b/docs/includes/special.txt
index 36c728c..e006a69 100644
--- a/docs/includes/special.txt
+++ b/docs/includes/special.txt
@@ -13,5 +13,6 @@
* link:poll.html[Poll Process]
* link:clock.html[Clock State Machine]
* link:leap.html[Leap Second Processing]
+* link:leapsmear.html[Leap Smearing]
* link:sitemap.html[Site Map]
diff --git a/docs/leap.txt b/docs/leap.txt
index 9833b6b..c4fc7ea 100644
--- a/docs/leap.txt
+++ b/docs/leap.txt
@@ -69,6 +69,8 @@ public-key cryptography feature is installed, the TAI offset is
automatically propagated along with other cryptographic media to
dependent servers and clients.
+You may also wish to read the page on link:leapsmer.html[leap smearing].
+
include::includes/footer.txt[]
'''''
diff --git a/docs/leapsmear.txt b/docs/leapsmear.txt
new file mode 100644
index 0000000..4db434d
--- /dev/null
+++ b/docs/leapsmear.txt
@@ -0,0 +1,281 @@
+= Leap Second Smearing with NTP=
+
+By Martin Burnicki
+with some edits by Harlan Stenn
+
+The NTP software protocol and its reference implementation, ntpd, were
+originally designed to distribute UTC time over a network as accurately as
+possible.
+
+Unfortunately, leap seconds are scheduled to be inserted into or deleted
+from the UTC time scale in irregular intervals to keep the UTC time scale
+synchronized with the Earth rotation. Deletions haven't happened, yet, but
+insertions have happened over 30 times.
+
+The problem is that POSIX requires 86400 seconds in a day, and there is no
+prescribed way to handle leap seconds in POSIX.
+
+Whenever a leap second is to be handled ntpd either:
+
+* passes the leap second announcement down to the OS kernel (if the OS
+supports this) and the kernel handles the leap second automatically, or
+
+* applies the leap second correction itself.
+
+NTP servers also pass a leap second warning flag down to their clients via
+the normal NTP packet exchange, so clients also become aware of an
+approaching leap second, and can handle the leap second appropriately.
+
+
+== The Problem on Unix-like Systems ==
+
+If a leap second is to be inserted then in most Unix-like systems the OS
+kernel just steps the time back by 1 second at the beginning of the leap
+second, so the last second of the UTC day is repeated and thus duplicate
+timestamps can occur.
+
+Unfortunately there are lots of applications which get confused it the
+system time is stepped back, e.g. due to a leap second insertion. Thus,
+many users have been looking for ways to avoid this, and tried to introduce
+workarounds which may work properly, or not.
+
+So even though these Unix kernels normally can handle leap seconds, the way
+they do this is not optimal for applications.
+
+One good way to handle the leap second is to use ntp_gettime() instead of
+the usual calls, because ntp_gettime() includes a "clock state" variable
+that will actually tell you if the time you are receiving is OK or not, and
+if it is OK, if the current second is an in-progress leap second. But even
+though this mechanism has been available for about 20 years' time, almost
+nobody uses it.
+
+
+== NTP Client for Windows Contains a Workaround ==
+
+The Windows system time knows nothing about leap seconds, so for many years
+the Windows port of ntpd provides a workaround where the system time is
+slewed by the client to compensate the leap second.
+
+Thus it is not required to use a smearing NTP server for Windows clients,
+but of course the smearing server approach also works.
+
+
+== The Leap Smear Approach ==
+
+Due to the reasons mentioned above some support for leap smearing has
+recently been implemented in ntpd. This means that to insert a leap second
+an NTP server adds a certain increasing "smear" offset to the real UTC time
+sent to its clients, so that after some predefined interval the leap second
+offset is compensated. The smear interval should be long enough,
+e.g. several hours, so that NTP clients can easily follow the clock drift
+caused by the smeared time.
+
+During the period while the leap smear is being performed, ntpd will include
+a specially-formatted 'refid' in time packets that contain "smeared" time.
+This refid is of the form 254.x.y.z, where x.y.z are 24 encoded bits of the
+smear value.
+
+With this approach the time an NTP server sends to its clients still matches
+UTC before the leap second, up to the beginning of the smear interval, and
+again corresponds to UTC after the insertion of the leap second has
+finished, at the end of the smear interval. By examining the first byte of
+the refid, one can also determine if the server is offering smeared time or
+not.
+
+Of course, clients which receive the "smeared" time from an NTP server don't
+have to (and even must not) care about the leap second anymore. Smearing is
+just transparent to the clients, and the clients don't even notice there's a
+leap second.
+
+
+== Pros and Cons of the Smearing Approach ==
+
+The disadvantages of this approach are:
+
+* During the smear interval the time provided by smearing NTP servers
+differs significantly from UTC, and thus from the time provided by normal,
+non-smearing NTP servers. The difference can be up to 1 second, depending
+on the smear algorithm.
+
+* Since smeared time differs from true UTC, and many applications require
+correct legal time (UTC), there may be legal consequences to using smeared
+time. Make sure you check to see if this requirement affects you.
+
+However, for applications where it's only important that all computers have
+the same time and a temporary offset of up to 1 s to UTC is acceptable, a
+better approach may be to slew the time in a well defined way, over a
+certain interval, which is what we call smearing the leap second.
+
+
+== The Motivation to Implement Leap Smearing ==
+
+Here is some historical background for ntpd, related to smearing/slewing
+time.
+
+Up to ntpd 4.2.4, if kernel support for leap seconds was either not
+available or was not enabled, ntpd didn't care about the leap second at all.
+So if ntpd was run with -x and thus kernel support wasn't used, ntpd saw a
+sudden 1 s offset after the leap second and normally would have stepped the
+time by -1 s a few minutes later. However, 'ntpd -x' does not step the time
+but "slews" the 1-second correction, which takes 33 minutes and 20 seconds
+to complete. This could be considered a bug, but certainly this was only an
+accidental behavior.
+
+However, as we learned in the discussion in http://bugs.ntp.org/2745, this
+behavior was very much appreciated since indeed the time was never stepped
+back, and even though the start of the slewing was somewhat undefined and
+depended on the poll interval. The system time was off by 1 second for
+several minutes before slewing even started.
+
+In ntpd 4.2.6 some code was added which let ntpd step the time at UTC
+midnight to insert a leap second, if kernel support was not used.
+Unfortunately this also happened if ntpd was started with -x, so the folks
+who expected that the time was never stepped when ntpd was run with -x found
+this wasn't true anymore, and again from the discussion in NTP bug 2745 we
+learn that there were even some folks who patched ntpd to get the 4.2.4
+behavior back.
+
+In 4.2.8 the leap second code was rewritten and some enhancements were
+introduced, but the resulting code still showed the behavior of 4.2.6,
+i.e. ntpd with -x would still step the time. This has only recently been
+fixed in the current ntpd stable code, but this fix is only available with a
+certain patch level of ntpd 4.2.8.
+
+So a possible solution for users who were looking for a way to come over the
+leap second without the time being stepped could have been to check the
+version of ntpd installed on each of their systems. If it's still 4.2.4 be
+sure to start the client ntpd with -x. If it's 4.2.6 or 4.2.8 it won't work
+anyway except if you had a patched ntpd version instead of the original
+version. So you'd need to upgrade to the current -stable code to be able to
+run ntpd with -x and get the desired result, so you'd still have the
+requirement to check/update/configure every single machine in your network
+that runs ntpd.
+
+Google's leap smear approach is a very efficient solution for this, for
+sites that do not require correct timestamps for legal purposes. You just
+have to take care that your NTP servers support leap smearing and configure
+those few servers accordingly. If the smear interval is long enough so that
+NTP clients can follow the smeared time it doesn't matter at all which
+version of ntpd is installed on a client machine, it just works, and it even
+works around kernel bugs due to the leap second.
+
+Since all clients follow the same smeared time the time difference between
+the clients during the smear interval is as small as possible, compared to
+the -x approach. The current leap second code in ntpd determines the point
+in system time when the leap second is to be inserted, and given a
+particular smear interval it's easy to determine the start point of the
+smearing, and the smearing is finished when the leap second ends, i.e. the
+next UTC day begins.
+
+The maximum error doesn't exceed what you'd get with the old smearing caused
+by -x in ntpd 4.2.4, so if users could accept the old behavior they would
+even accept the smearing at the server side.
+
+In order to affect the local timekeeping as little as possible the leap
+smear support currently implemented in ntpd does not affect the internal
+system time at all. Only the timestamps and refid in outgoing reply packets
+*to clients* are modified by the smear offset, so this makes sure the basic
+functionality of ntpd is not accidentally broken. Also peer packets
+exchanged with other NTP servers are based on the real UTC system time and
+the normal refid, as usual.
+
+The leap smear implementation is optionally available in ntp-4.2.8p3 and
+later, and the changes can be tracked via http://bugs.ntp.org/2855.
+
+
+== Using NTP's Leap Second Smearing ==
+
+* Leap Second Smearing MUST NOT be used for public servers, e.g. servers
+provided by metrology institutes, or servers participating in the NTP pool
+project. There would be a high risk that NTP clients get the time from a
+mixture of smearing and non-smearing NTP servers which could result in
+undefined client behavior. Instead, leap second smearing should only be
+configured on time servers providing dedicated clients with time, if all
+those clients can accept smeared time.
+
+* Leap Second Smearing is NOT configured by default. The only way to get
+this behavior is to invoke the ./configure script from the NTP source code
+package with the --enable-leap-smear parameter before the executables are
+built.
+
+* Even if ntpd has been compiled to enable leap smearing support, leap
+smearing is only done if explicitly configured.
+
+* The leap smear interval should be at least several hours' long, and up to
+1 day (86400s). If the interval is too short then the applied smear offset
+is applied too quickly for clients to follow. 86400s (1 day) is a good
+choice.
+
+* If several NTP servers are set up for leap smearing then the *same* smear
+interval should be configured on each server.
+
+* Smearing NTP servers DO NOT send a leap second warning flag to client time
+requests. Since the leap second is applied gradually the clients don't even
+notice there's a leap second being inserted, and thus there will be no log
+message or similar related to the leap second be visible on the clients.
+
+* Since clients don't (and must not) become aware of the leap second at all,
+clients getting the time from a smearing NTP server MUST NOT be configured
+to use a leap second file. If they had a leap second file they would apply
+the leap second twice: the smeared one from the server, plus another one
+inserted by themselves due to the leap second file. As a result, the
+additional correction would soon be detected and corrected/adjusted.
+
+* Clients MUST NOT be configured to poll both smearing and non-smearing NTP
+servers at the same time. During the smear interval they would get
+different times from different servers and wouldn't know which server(s) to
+accept.
+
+
+== Setting Up A Smearing NTP Server ==
+
+If an NTP server should perform leap smearing then the leap smear interval
+(in seconds) needs to be specified in the NTP configuration file ntp.conf,
+e.g.:
+
+--------------------------------
+leapsmearinterval 86400
+--------------------------------
+
+Please keep in mind the leap smear interval should be between several and 24
+hours' long. With shorter values clients may not be able to follow the
+drift caused by the smeared time, and with longer values the discrepancy
+between system time and UTC will cause more problems when reconciling
+timestamp differences.
+
+When ntpd starts and a smear interval has been specified then a log message
+is generated, e.g.:
+
+----------------------------------------------------------------
+ntpd[31120]: config: leap smear interval 86400 s
+----------------------------------------------------------------
+
+While ntpd is running with a leap smear interval specified the command:
+
+--------------------------------
+ntpq -c rv
+--------------------------------
+
+reports the smear status, e.g.:
+
+--------------------------------
+# ntpq -c rv
+associd=0 status=4419 leap_add_sec, sync_uhf_radio, 1 event, leap_armed,
+version="ntpd 4.2.8p3-RC1 at 1.3349-o Mon Jun 22 14:24:09 UTC 2015 (26)",
+processor="i586", system="Linux/3.7.1", leap=01, stratum=1,
+precision=-18, rootdelay=0.000, rootdisp=1.075, refid=MRS,
+reftime=d93dab96.09666671 Tue, Jun 30 2015 23:58:14.036,
+clock=d93dab9b.3386a8d5 Tue, Jun 30 2015 23:58:19.201, peer=2335,
+tc=3, mintc=3, offset=-0.097015, frequency=44.627, sys_jitter=0.003815,
+clk_jitter=0.451, clk_wander=0.035, tai=35, leapsec=201507010000,
+expire=201512280000, leapsmearinterval=86400, leapsmearoffset=-932.087
+--------------------------------
+
+In the example above 'leapsmearinterval' reports the configured leap smear
+interval all the time, while the 'leapsmearoffset' value is 0 outside the
+interval and increases from 0 to -1000 ms over the interval. So this can be
+used to monitor if and how the time sent to clients is smeared. With a
+leapsmearoffset of -.932087, the refid reported in smeared packets would be
+254.196.88.176.
+
+// end
diff --git a/include/ntpd.h b/include/ntpd.h
index 3ab6dd6..6eadcaa 100644
--- a/include/ntpd.h
+++ b/include/ntpd.h
@@ -221,6 +221,7 @@ extern void receive (struct recvbuf *);
extern void peer_clear (struct peer *, const char *);
extern void process_packet (struct peer *, struct pkt *, u_int);
extern void clock_select (void);
+extern void set_sys_leap (uint8_t);
extern u_long leapsec; /* seconds to next leap (proximity class) */
extern int leapdif; /* TAI difference step at next leap second*/
@@ -511,6 +512,11 @@ extern u_long current_time; /* seconds since startup */
extern u_long timer_timereset;
extern u_long timer_overflows;
extern u_long timer_xmtcalls;
+extern bool leap_sec_in_progress;
+#ifdef ENABLE_LEAP_SMEAR
+extern struct leap_smear_info leap_smear;
+extern int leap_smear_intv;
+#endif
#ifdef SYS_WINNT
HANDLE WaitableTimerHandle;
#endif
diff --git a/include/refidsmear.h b/include/refidsmear.h
new file mode 100644
index 0000000..9c0f245
--- /dev/null
+++ b/include/refidsmear.h
@@ -0,0 +1,3 @@
+
+extern l_fp convertRefIDToLFP(uint32_t r);
+extern uint32_t convertLFPToRefID(l_fp num);
diff --git a/libntp/refidsmear.c b/libntp/refidsmear.c
new file mode 100644
index 0000000..fef428e
--- /dev/null
+++ b/libntp/refidsmear.c
@@ -0,0 +1,58 @@
+#include <config.h>
+
+#include <ntp.h>
+#include <ntp_fp.h>
+#include <refidsmear.h>
+
+/*
+ * we want to test a refid format of:
+ * 254.x.y.x
+ *
+ * where x.y.z are 24 bits containing 2 (signed) integer bits
+ * and 22 fractional bits.
+ *
+ */
+
+
+l_fp
+convertRefIDToLFP(uint32_t r)
+{
+ l_fp temp;
+
+ r = ntohl(r);
+
+ // printf("%03d %08x: ", (r >> 24) & 0xFF, (r & 0x00FFFFFF) );
+
+ temp.l_uf = (r << 10); /* 22 fractional bits */
+
+ temp.l_ui = (r >> 22) & 0x3;
+ temp.l_ui |= ~(temp.l_ui & 2) + 1;
+
+ return temp;
+}
+
+
+uint32_t
+convertLFPToRefID(l_fp num)
+{
+ uint32_t temp;
+
+ /* round the input with the highest bit to shift out from the
+ * fraction, then keep just two bits from the integral part.
+ *
+ * TODO: check for overflows; should we clamp/saturate or just
+ * complain?
+ */
+ L_ADDUF(&num, 0x200);
+ num.l_ui &= 3;
+
+ /* combine integral and fractional part to 24 bits */
+ temp = (num.l_ui << 22) | (num.l_uf >> 10);
+
+ /* put in the leading 254.0.0.0 */
+ temp |= UINT32_C(0xFE000000);
+
+ // printf("%03d %08x: ", (temp >> 24) & 0xFF, (temp & 0x00FFFFFF) );
+
+ return htonl(temp);
+}
diff --git a/libntp/wscript b/libntp/wscript
index 256e635..0cdc186 100644
--- a/libntp/wscript
+++ b/libntp/wscript
@@ -46,6 +46,7 @@ def build(ctx):
"prettydate.c",
"recvbuff.c",
"refnumtoa.c",
+ "refidsmear.c",
"socket.c",
"socktoa.c",
"socktohost.c",
diff --git a/ntpd/ntp_config.c b/ntpd/ntp_config.c
index bc54565..efc51a5 100644
--- a/ntpd/ntp_config.c
+++ b/ntpd/ntp_config.c
@@ -3468,6 +3468,13 @@ config_vars(
stats_config(STATS_LEAP_FILE, curr_var->value.s);
break;
+#ifdef ENABLE_LEAP_SMEAR
+ case T_Leapsmearinterval:
+ leap_smear_intv = curr_var->value.i;
+ msyslog(LOG_INFO, "config: leap smear interval %i s", leap_smear_intv);
+ break;
+#endif
+
case T_Pidfile:
stats_config(STATS_PID_FILE, curr_var->value.s);
break;
diff --git a/ntpd/ntp_control.c b/ntpd/ntp_control.c
index 5f9c40f..e9f69f4 100644
--- a/ntpd/ntp_control.c
+++ b/ntpd/ntp_control.c
@@ -218,7 +218,9 @@ static const struct ctl_proc control_codes[] = {
#define CS_TIMER_XMTS 87
#define CS_FUZZ 88
#define CS_WANDER_THRESH 89
-#define CS_MAX_NOAUTOKEY CS_WANDER_THRESH
+#define CS_LEAPSMEARINTV 90
+#define CS_LEAPSMEAROFFS 91
+#define CS_MAX_NOAUTOKEY CS_LEAPSMEAROFFS
#ifdef ENABLE_AUTOKEY
#define CS_FLAGS (1 + CS_MAX_NOAUTOKEY)
#define CS_HOST (2 + CS_MAX_NOAUTOKEY)
@@ -414,6 +416,8 @@ static const struct ctl_var sys_var[] = {
{ CS_TIMER_XMTS, RO, "timer_xmts" }, /* 87 */
{ CS_FUZZ, RO, "fuzz" }, /* 88 */
{ CS_WANDER_THRESH, RO, "clk_wander_threshold" }, /* 89 */
+ { CS_LEAPSMEARINTV, RO, "leapsmearinterval" }, /* 90 */
+ { CS_LEAPSMEAROFFS, RO, "leapsmearoffset" }, /* 91 */
#ifdef ENABLE_AUTOKEY
{ CS_FLAGS, RO, "flags" }, /* 1 + CS_MAX_NOAUTOKEY */
{ CS_HOST, RO, "host" }, /* 2 + CS_MAX_NOAUTOKEY */
@@ -1893,6 +1897,19 @@ ctl_putsys(
break;
}
+#ifdef ENABLE_LEAP_SMEAR
+ case CS_LEAPSMEARINTV:
+ if (leap_smear_intv > 0)
+ ctl_putuint(sys_var[CS_LEAPSMEARINTV].text, leap_smear_intv);
+ break;
+
+ case CS_LEAPSMEAROFFS:
+ if (leap_smear_intv > 0)
+ ctl_putdbl(sys_var[CS_LEAPSMEAROFFS].text,
+ leap_smear.doffset * 1e3);
+ break;
+#endif /* ENABLE_LEAP_SMEAR */
+
case CS_RATE:
ctl_putuint(sys_var[CS_RATE].text, ntp_minpoll);
break;
diff --git a/ntpd/ntp_io.c b/ntpd/ntp_io.c
index fc50e74..7686d7a 100644
--- a/ntpd/ntp_io.c
+++ b/ntpd/ntp_io.c
@@ -73,6 +73,14 @@ extern int listen_to_virtual_ips;
#endif
int qos = IPTOS_DSCP_EF; /* QoS RFC3246 */
+#ifdef ENABLE_LEAP_SMEAR
+/* TODO burnicki: This should be moved to ntp_timer.c, but if we do so
+ * we get a linker error. Since we're running out of time before the leap
+ * second occurs, we let it here where it just works.
+ */
+int leap_smear_intv;
+#endif
+
/*
* NIC rule entry
*/
diff --git a/ntpd/ntp_leapsec.h b/ntpd/ntp_leapsec.h
index 17548f7..0dfa5fa 100644
--- a/ntpd/ntp_leapsec.h
+++ b/ntpd/ntp_leapsec.h
@@ -101,6 +101,22 @@ struct leap_signature {
};
typedef struct leap_signature leap_signature_t;
+#ifdef ENABLE_LEAP_SMEAR
+
+struct leap_smear_info {
+ bool enabled; /* true if smearing is generally enabled */
+ bool in_progress; /* true if smearing is in progress, i.e. the offset has been computed */
+ double doffset; /* the current smear offset as double */
+ l_fp offset; /* the current smear offset */
+ uint32_t t_offset; /* the current time for which a smear offset has been computed */
+ long interval; /* smear interval, in [s], should be at least some hours */
+ double intv_start; /* start time of the smear interval */
+ double intv_end; /* end time of the smear interval */
+};
+typedef struct leap_smear_info leap_smear_info_t;
+
+#endif /* ENABLE_LEAP_SMEAR */
+
#define LSPROX_NOWARN 0 /* clear radar screen */
#define LSPROX_SCHEDULE 1 /* less than 1 month to target*/
diff --git a/ntpd/ntp_parser.y b/ntpd/ntp_parser.y
index e77e5cc..1622345 100644
--- a/ntpd/ntp_parser.y
+++ b/ntpd/ntp_parser.y
@@ -141,6 +141,7 @@
%token <Integer> T_Kod
%token <Integer> T_Mssntp
%token <Integer> T_Leapfile
+%token <Integer> T_Leapsmearinterval
%token <Integer> T_Limited
%token <Integer> T_Link
%token <Integer> T_Listen
@@ -1214,6 +1215,15 @@ misc_cmd_int_keyword
: T_Dscp
;
+misc_cmd_int_keyword
+ : T_Leapsmearinterval
+ {
+#ifndef LEAP_SMEAR
+ yyerror("Built without LEAP_SMEAR support.");
+#endif
+ }
+ ;
+
misc_cmd_str_keyword
: T_Ident
| T_Leapfile
diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c
index f6dfb95..f19e21b 100644
--- a/ntpd/ntp_proto.c
+++ b/ntpd/ntp_proto.c
@@ -12,6 +12,7 @@
#include "ntp_control.h"
#include "ntp_leapsec.h"
#include "ntp_intercept.h"
+#include "refidsmear.h"
#include <string.h>
#include <stdio.h>
@@ -60,6 +61,7 @@ typedef struct peer_select_tag {
* times are in seconds.
*/
uint8_t sys_leap; /* system leap indicator */
+uint8_t xmt_leap; /* leap indicator sent in client requests */
uint8_t sys_stratum; /* system stratum */
int8_t sys_precision; /* local clock precision (log2 s) */
double sys_rootdelay; /* roundtrip delay to primary source */
@@ -68,6 +70,11 @@ uint32_t sys_refid; /* reference id (network byte order) */
l_fp sys_reftime; /* last update time */
struct peer *sys_peer; /* current peer */
+#ifdef ENABLE_LEAP_SMEAR
+struct leap_smear_info leap_smear;
+#endif
+bool leap_sec_in_progress;
+
/*
* Rate controls. Leaky buckets are used to throttle the packet
* transmission rates in order to protect busy servers such as at NIST
@@ -149,6 +156,34 @@ void pool_name_resolved (int, int, void *, const char *,
#endif /* USE_WORKER */
+void
+set_sys_leap(u_char new_sys_leap) {
+ sys_leap = new_sys_leap;
+ xmt_leap = sys_leap;
+
+ /*
+ * Under certain conditions we send faked leap bits to clients, so
+ * eventually change xmt_leap below, but never change LEAP_NOTINSYNC.
+ */
+ if (xmt_leap != LEAP_NOTINSYNC) {
+ if (leap_sec_in_progress) {
+ /* always send "not sync" */
+ xmt_leap = LEAP_NOTINSYNC;
+ }
+#ifdef ENABLE_LEAP_SMEAR
+ else {
+ /*
+ * If leap smear is enabled in general we must
+ * never send a leap second warning to clients, so
+ * make sure we only send "in sync".
+ */
+ if (leap_smear.enabled)
+ xmt_leap = LEAP_NOWARNING;
+ }
+#endif /* ENABLE_LEAP_SMEAR */
+ }
+}
+
/*
* transmit - transmit procedure called by poll timeout
*/
@@ -1935,7 +1970,7 @@ clock_update(
*/
case 2:
clear_all();
- sys_leap = LEAP_NOTINSYNC;
+ set_sys_leap(LEAP_NOTINSYNC);
sys_stratum = STRATUM_UNSPEC;
memcpy(&sys_refid, "STEP", REFIDLEN);
sys_rootdelay = 0;
@@ -1956,7 +1991,7 @@ clock_update(
* process.
*/
if (sys_leap == LEAP_NOTINSYNC) {
- sys_leap = LEAP_NOWARNING;
+ set_sys_leap(LEAP_NOWARNING);
#ifdef ENABLE_AUTOKEY
if (crypto_flags)
crypto_update();
@@ -2450,7 +2485,7 @@ clock_select(void)
osys_peer = sys_peer;
sys_survivors = 0;
#ifdef ENABLE_LOCKCLOCK
- sys_leap = LEAP_NOTINSYNC;
+ set_sys_leap(LEAP_NOTINSYNC);
sys_stratum = STRATUM_UNSPEC;
memcpy(&sys_refid, "DOWN", REFIDLEN);
#endif /* ENABLE_LOCKCLOCK */
@@ -3429,6 +3464,15 @@ peer_xmit(
}
+#ifdef ENABLE_LEAP_SMEAR
+
+static void
+leap_smear_add_offs(l_fp *t, l_fp *t_recv) {
+ L_ADD(t, &leap_smear.offset);
+}
+
+#endif /* ENABLE_LEAP_SMEAR */
+
/*
* fast_xmit - Send packet for nonpersistent association. Note that
* neither the source or destination can be a broadcast address.
@@ -3490,6 +3534,21 @@ fast_xmit(
* This is a normal packet. Use the system variables.
*/
} else {
+#ifdef ENABLE_LEAP_SMEAR
+ /*
+ * Make copies of the variables which can be affected by smearing.
+ */
+ l_fp this_ref_time;
+ l_fp this_recv_time;
+#endif
+
+ /*
+ * If we are inside the leap smear interval we add the
+ * current smear offset to the packet receive time, to
+ * the packet transmit time, and eventually to the
+ * reftime to make sure the reftime isn't later than
+ * the transmit/receive times.
+ */
xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
PKT_VERSION(rpkt->li_vn_mode), xmode);
xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
@@ -3498,10 +3557,38 @@ fast_xmit(
xpkt.refid = sys_refid;
xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
xpkt.rootdisp = HTONS_FP(DTOUFP(sys_rootdisp));
+
+#ifdef ENABLE_LEAP_SMEAR
+ this_ref_time = sys_reftime;
+ if (leap_smear.in_progress) {
+ leap_smear_add_offs(&this_ref_time, NULL);
+ xpkt.refid = convertLFPToRefID(leap_smear.offset);
+ DPRINTF(2, ("fast_xmit: leap_smear.in_progress: refid %8x, smear %s\n",
+ ntohl(xpkt.refid),
+ lfptoa(&leap_smear.offset, 8)
+ ));
+ }
+ HTONL_FP(&this_ref_time, &xpkt.reftime);
+#else
HTONL_FP(&sys_reftime, &xpkt.reftime);
+#endif
+
xpkt.org = rpkt->xmt;
+
+#ifdef ENABLE_LEAP_SMEAR
+ this_recv_time = rbufp->recv_time;
+ if (leap_smear.in_progress)
+ leap_smear_add_offs(&this_recv_time, NULL);
+ HTONL_FP(&this_recv_time, &xpkt.rec);
+#else
HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+#endif
+
intercept_get_systime(__func__, &xmt_tx);
+#ifdef ENABLE_LEAP_SMEAR
+ if (leap_smear.in_progress)
+ leap_smear_add_offs(&xmt_tx, &this_recv_time);
+#endif
HTONL_FP(&xmt_tx, &xpkt.xmt);
}
diff --git a/ntpd/ntp_timer.c b/ntpd/ntp_timer.c
index 0ef4bf3..b6baa91 100644
--- a/ntpd/ntp_timer.c
+++ b/ntpd/ntp_timer.c
@@ -444,6 +444,16 @@ timer_clr_stats(void)
}
static void
+check_leap_sec_in_progress( const leap_result_t *lsdata ) {
+ bool prv_leap_sec_in_progress = leap_sec_in_progress;
+ leap_sec_in_progress = lsdata->tai_diff && (lsdata->ddist < 3);
+
+ /* if changed we may have to update the leap status sent to clients */
+ if (leap_sec_in_progress != prv_leap_sec_in_progress)
+ set_sys_leap(sys_leap);
+}
+
+static void
check_leapsec(
uint32_t now ,
const time_t * tpiv ,
@@ -471,11 +481,80 @@ check_leapsec(
leapsec_electric(0);
# endif
#endif
+#ifdef ENABLE_LEAP_SMEAR
+ leap_smear.enabled = (leap_smear_intv != 0);
+#endif
if (reset) {
lsprox = LSPROX_NOWARN;
leapsec_reset_frame();
memset(&lsdata, 0, sizeof(lsdata));
} else if (leapsec_query(&lsdata, now, tpiv)) {
+ DPRINTF(1, ("*** leapsec_query: fired %i, now %u (0x%08X), tai_diff %i, ddist %u\n",
+ fired, now, now, lsdata.tai_diff, lsdata.ddist));
+#ifdef ENABLE_LEAP_SMEAR
+ leap_smear.in_progress = false;
+ leap_smear.doffset = 0.0;
+
+ if (leap_smear.enabled) {
+ if (lsdata.tai_diff) {
+ if (leap_smear.interval == 0) {
+ leap_smear.interval = leap_smear_intv;
+ leap_smear.intv_end = lsdata.ttime.Q_s;
+ leap_smear.intv_start = leap_smear.intv_end - leap_smear.interval;
+ DPRINTF(1, ("*** leapsec_query: setting leap_smear interval %li, begin %.0f, end %.0f\n",
+ leap_smear.interval, leap_smear.intv_start, leap_smear.intv_end));
+ }
+ }
+ else {
+ if (leap_smear.interval)
+ DPRINTF(1, ("*** leapsec_query: clearing leap_smear interval\n"));
+ leap_smear.interval = 0;
+ }
+
+ if (leap_smear.interval) {
+ double dtemp = now;
+ if (dtemp >= leap_smear.intv_start && dtemp <= leap_smear.intv_end) {
+ double leap_smear_time = dtemp - leap_smear.intv_start;
+ /*
+ * For now we just do a linear interpolation over the smear interval
+ */
+#if 0
+ // linear interpolation
+ leap_smear.doffset = -(leap_smear_time * lsdata.tai_diff / leap_smear.interval);
+#else
+ // Google approach: lie(t) = (1.0 - cos(pi * t / w)) / 2.0
+ leap_smear.doffset = -((double) lsdata.tai_diff - cos( M_PI * leap_smear_time / leap_smear.interval)) / 2.0;
+#endif
+ /*
+ * TODO see if we're inside an
+ * inserted leap second, so we
+ * need to compute
+ * leap_smear.doffset = 1.0 -
+ * leap_smear.doffset
+ */
+ leap_smear.in_progress = true;
+#if 0 && defined( DEBUG )
+ msyslog(LOG_NOTICE, "*** leapsec_query: [%.0f:%.0f] (%li), now %u (%.0f), smear offset %.6f ms\n",
+ leap_smear.intv_start, leap_smear.intv_end, leap_smear.interval,
+ now, leap_smear_time, leap_smear.doffset);
+#else
+ DPRINTF(1, ("*** leapsec_query: [%.0f:%.0f] (%li), now %u (%.0f), smear offset %.6f ms\n",
+ leap_smear.intv_start, leap_smear.intv_end, leap_smear.interval,
+ now, leap_smear_time, leap_smear.doffset));
+#endif
+
+ }
+ }
+ }
+ else
+ leap_smear.interval = 0;
+
+ /*
+ * Update the current leap smear offset, eventually 0.0 if outside smear interval.
+ */
+ DTOLFP(leap_smear.doffset, &leap_smear.offset);
+#endif /* ENABLE_LEAP_SMEAR */
+
/* Full hit. Eventually step the clock, but always
* announce the leap event has happened.
*/
@@ -539,4 +618,6 @@ check_leapsec(
leapdif = lsdata.tai_diff;
else
leapdif = 0;
+
+ check_leap_sec_in_progress(&lsdata);
}
More information about the vc
mailing list