[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