]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
[Bug 2830] ntpd doesn't always transfer the correct TAI offset via autokey
authorJuergen Perlinger <perlinger@ntp.org>
Mon, 1 Jun 2015 05:24:52 +0000 (07:24 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Mon, 1 Jun 2015 05:24:52 +0000 (07:24 +0200)
  NTPD transfers the current TAI (instead of an announcement) now. This might still needed improvement.

bk: 556beca4yZCl5kSVCreQdypW4aKySA

ChangeLog
ntpd/ntp_crypto.c
ntpd/ntp_leapsec.c
ntpd/ntp_leapsec.h
tests/ntpd/leapsec.cpp

index f849c79b7c6bd0d0465f2ec70646d2709614cfa7..3225ce17481c7d1d333fd5cf961cc6974e8a088c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -40,9 +40,8 @@
 * [Bug 2822] New leap column in sntp broke NTP::Util.pm.
 * [Bug 2825] Quiet file installation in html/ .
 * [Bug 2830] ntpd doesn't always transfer the correct TAI offset via autokey
-   NTP does *not* transfer the TAI offset, but a leap second announcement.
-   Handling of these announcements and stepping over leap seconds
-   needed improvement.
+   NTPD transfers the current TAI (instead of an announcement) now.
+   This might still needed improvement.
 * Add an assert to the ntpq ifstats code.
 * Clean up the RLIMIT_STACK code.
 * Improve the ntpq documentation around the controlkey keyid.
index 5795ae8dfdd799f7b0e9a4934c22deb8b6b355ac..6e0e1eb4b28783b016568e6533cb5656ecdba0b7 100644 (file)
@@ -992,34 +992,43 @@ crypto_recv(
                            XEVNT_OK)
                                break;
 
-                       /*
-                        * If the packet leap values are more recent
-                        * than the stored ones, install the new leap
-                        * values and recompute the signatures.
+                       /* Check if we can update the basic TAI offset
+                        * for our current leap frame. This is a hack
+                        * and ignores the time stamps in the autokey
+                        * message.
                         */
-                       if (leapsec_add_fix(ntohl(ep->pkt[0]),
-                                           ntohl(ep->pkt[1]),
-                                           ntohl(ep->pkt[2]),
-                                           NULL))
+                       if (vallen == 0) {
+                               /* NOP */
+                       } else if (vallen != 3*sizeof(uint32_t)) {
+#ifdef DEBUG
+                               if (debug)
+                                       printf("crypto_recv: CRYPTO_LEAP: bad value size %u\n", vallen);
+#endif
+                       } else if (sys_leap == LEAP_NOTINSYNC) {
+#ifdef DEBUG
+                               if (debug)
+                                       printf("crypto_recv: CRYPTO_LEAP: not in sync, TAI ignored\n");
+#endif
+                       } else if ( ! leapsec_autokey_tai(ntohl(ep->pkt[0]),
+                                               rbufp->recv_time.l_ui, NULL))
                        {
-                               leap_signature_t lsig;
-
-                               leapsec_getsig(&lsig);
-                               tai_leap.tstamp = ep->tstamp;
-                               tai_leap.fstamp = ep->fstamp;
-                               tai_leap.vallen = ep->vallen;
-                               crypto_update();
-                               mprintf_event(EVNT_TAI, peer,
-                                   "%d leap %s expire %s", lsig.taiof,
-                                   fstostr(lsig.ttime),
-                                   fstostr(lsig.etime));
+#ifdef DEBUG
+                               if (debug)
+                                       printf("crypto_recv: CRYPTO_LEAP: TAI not updated\n");
+#endif
                        }
+                       
+                       tai_leap.tstamp = ep->tstamp;
+                       tai_leap.fstamp = ep->fstamp;
+                       crypto_update();
+                       mprintf_event(EVNT_TAI, peer,
+                                     "%d seconds", ntohl(ep->pkt[0]));
                        peer->crypto |= CRYPTO_FLAG_LEAP;
                        peer->flash &= ~TEST8;
                        snprintf(statstr, sizeof(statstr),
-                           "leap TAI offset %d at %u expire %u fs %u",
-                           ntohl(ep->pkt[0]), ntohl(ep->pkt[1]),
-                           ntohl(ep->pkt[2]), ntohl(ep->fstamp));
+                                "leap TAI offset %d at %u expire %u fs %u",
+                                ntohl(ep->pkt[0]), ntohl(ep->pkt[1]),
+                                ntohl(ep->pkt[2]), ntohl(ep->fstamp));
                        record_crypto_stats(&peer->srcadr, statstr);
 #ifdef DEBUG
                        if (debug)
@@ -1858,7 +1867,7 @@ crypto_update(void)
        char    statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
        u_int32 *ptr;
        u_int   len;
-       leap_signature_t lsig;
+       leap_result_t leap_data;
 
        hostval.tstamp = htonl(crypto_time());
        if (hostval.tstamp == 0)
@@ -1906,15 +1915,28 @@ crypto_update(void)
         */
        tai_leap.tstamp = hostval.tstamp;
        tai_leap.fstamp = hostval.fstamp;
-       len = 3 * sizeof(u_int32);
-       if (tai_leap.ptr == NULL)
-               tai_leap.ptr = emalloc(len);
-       tai_leap.vallen = htonl(len);
-       ptr = (u_int32 *)tai_leap.ptr;
-       leapsec_getsig(&lsig);
-       ptr[0] = htonl(lsig.taiof);
-       ptr[1] = htonl(lsig.ttime);
-       ptr[2] = htonl(lsig.etime);
+       if ( ! leapsec_frame(&leap_data))
+               leap_data.tai_offs = 0;
+
+       if (leap_data.tai_offs != 0) { /* might be better with > 10... */
+               len = 3 * sizeof(u_int32);
+               if (tai_leap.ptr == NULL || ntohl(tai_leap.vallen) != len) {
+                       free(tai_leap.ptr);
+                       tai_leap.ptr = emalloc(len);
+                       tai_leap.vallen = htonl(len);
+               }
+               ptr = (u_int32 *)tai_leap.ptr;
+               ptr[0] = htonl(leap_data.tai_offs);
+               ptr[1] = htonl(leap_data.ebase.d_s.lo);
+               if (leap_data.ttime.d_s.hi >= 0)
+                       ptr[2] = htonl(leap_data.ttime.D_s.lo -  7*86400);
+               else
+                       ptr[2] = htonl(leap_data.ebase.D_s.lo + 25*86400);
+       } else {
+               free(tai_leap.ptr);
+               tai_leap.ptr = NULL;
+               tai_leap.vallen = 0;
+       }
        if (tai_leap.sig == NULL)
                tai_leap.sig = emalloc(sign_siglen);
        EVP_SignInit(&ctx, sign_digest);
@@ -1922,8 +1944,7 @@ crypto_update(void)
        EVP_SignUpdate(&ctx, tai_leap.ptr, len);
        if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey))
                tai_leap.siglen = htonl(sign_siglen);
-       if (lsig.ttime > 0)
-               crypto_flags |= CRYPTO_FLAG_TAI;
+       crypto_flags |= CRYPTO_FLAG_TAI;
        snprintf(statstr, sizeof(statstr), "signature update ts %u",
            ntohl(hostval.tstamp)); 
        record_crypto_stats(NULL, statstr);
index 099d7bfc29303e4420dba61cc23e5fc6dfcb8bf9..90e053eb6c46a62e3d79a758dc76bf26edc4ab2a 100644 (file)
@@ -89,6 +89,8 @@ static char * get_line(leapsec_reader, void*, char*, size_t);
 static char * skipws(const char*);
 static int    parsefail(const char * cp, const char * ep);
 static void   reload_limits(leap_table_t*, const vint64*);
+static void   fetch_leap_era(leap_era_t*, const leap_table_t*,
+                            const vint64*);
 static int    betweenu32(uint32_t, uint32_t, uint32_t);
 static void   reset_times(leap_table_t*);
 static int    leapsec_add(leap_table_t*, const vint64*, int);
@@ -335,6 +337,8 @@ leapsec_query(
        }
 
        qr->tai_offs = pt->head.this_tai;
+       qr->ebase    = pt->head.ebase;
+       qr->ttime    = pt->head.ttime;
 
        /* If before the next scheduling alert, we're done. */
        if (ucmpv64(&ts64, &pt->head.stime) < 0)
@@ -344,7 +348,6 @@ leapsec_query(
        due32 = pt->head.dtime.D_s.lo;
 
        qr->tai_diff  = pt->head.next_tai - pt->head.this_tai;
-       qr->ttime     = pt->head.ttime;
        qr->ddist     = due32 - ts32;
        qr->dynamic   = pt->head.dynls;
        qr->proximity = LSPROX_SCHEDULE;
@@ -362,6 +365,22 @@ leapsec_query(
        return fired;
 }
 
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_query_era(
+       leap_era_t *   qr   ,
+       uint32_t       ntpts,
+       const time_t * pivot)
+{
+       const leap_table_t * pt;
+       vint64               ts64;
+       
+       pt   = leapsec_get_table(FALSE);
+       ts64 = ntpcal_ntp_to_ntp(ntpts, pivot);
+       fetch_leap_era(qr, pt, &ts64);
+       return TRUE;
+}
+
 /* ------------------------------------------------------------------ */
 int/*BOOL*/
 leapsec_frame(
@@ -376,6 +395,7 @@ leapsec_frame(
 
        qr->tai_offs = pt->head.this_tai;
        qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
+       qr->ebase    = pt->head.ebase;
        qr->ttime    = pt->head.ttime;
        qr->dynamic  = pt->head.dynls;
 
@@ -574,6 +594,7 @@ leapsec_daystolive(
 }
 
 /* ------------------------------------------------------------------ */
+#if 0 /* currently unused -- possibly revived later */
 int/*BOOL*/
 leapsec_add_fix(
        int            total,
@@ -606,6 +627,7 @@ leapsec_add_fix(
 
        return leapsec_set_table(pt);
 }
+#endif
 
 /* ------------------------------------------------------------------ */
 int/*BOOL*/
@@ -623,6 +645,68 @@ leapsec_add_dyn(
                && leapsec_set_table(pt));
 }
 
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_autokey_tai(
+       int            tai_offset,
+       uint32_t       ntpnow    ,
+       const time_t * pivot     )
+{
+       leap_table_t * pt;
+       leap_era_t     era;
+       vint64         now64;
+       int            idx;
+       
+       (void)tai_offset;
+       pt = leapsec_get_table(FALSE);
+       
+       /* Bail out if the basic offset is not zero */
+       if (pt->head.base_tai != 0)
+               return FALSE;
+
+       /* If there's already data in the table, check if an update is
+        * possible. Update is impossible if there are static entries
+        * (since this indicates a valid leapsecond file) or if we're
+        * too close to a leapsecond transition: We do not know on what
+        * side the transition the sender might have been, so we use a
+        * dead zone around the transition.
+        */
+               
+       /* Check for static entries */
+       for (idx = 0; idx != pt->head.size; idx++)
+               if ( ! pt->info[idx].dynls)
+                       return FALSE;
+
+       /* get the fulll time stamp and leap era for it */
+       now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
+       fetch_leap_era(&era, pt, &now64);
+       
+       /* check the limits with 20s dead band */
+       era.ebase = addv64i32(&era.ebase,  20);
+       if (ucmpv64(&now64, &era.ebase) < 0)
+               return FALSE;
+
+       era.ttime = addv64i32(&era.ttime, -20);
+       if (ucmpv64(&now64, &era.ttime) > 0)
+               return FALSE;
+       
+       /* Here we can proceed. Calculate the delta update. */
+       tai_offset -= era.taiof;
+
+       /* Shift the header info offsets. */
+       pt->head.base_tai += tai_offset;
+       pt->head.this_tai += tai_offset;
+       pt->head.next_tai += tai_offset;
+
+       /* Shift table entry offsets (if any) */
+       for (idx = 0; idx != pt->head.size; idx++)
+               pt->info[idx].taiof += tai_offset;
+
+       /* claim success... */
+       return TRUE;
+}
+
+
 /* =====================================================================
  * internal helpers
  */
@@ -800,6 +884,37 @@ reload_limits(
        }
 }
 
+/* [internal] fetch the leap era for a given time stamp.
+ * This is a cut-down version the algorithm used to reload the table
+ * limits, but it does not update any global state and provides just the
+ * era information for a given time stamp.
+ */
+static void
+fetch_leap_era(
+       leap_era_t         * into,
+       const leap_table_t * pt  ,
+       const vint64       * ts  )
+{
+       int idx;
+
+       /* Simple search loop, also works with empty table. */
+       for (idx = 0; idx != pt->head.size; idx++)
+               if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
+                       break;
+       /* fetch era data, keeping an eye on boundary conditions */
+       if (idx >= pt->head.size) {
+               memset(&into->ebase, 0x00, sizeof(vint64));
+               into->taiof = pt->head.base_tai;
+       } else {
+               into->ebase = pt->info[idx].ttime;
+               into->taiof = pt->info[idx].taiof;
+       }
+       if (--idx >= 0)
+               into->ttime = pt->info[idx].ttime;
+       else
+               memset(&into->ttime, 0xFF, sizeof(vint64));
+}
+
 /* [internal] Take a time stamp and create a leap second frame for
  * it. This will schedule a leap second for the beginning of the next
  * month, midnight UTC. The 'insert' argument tells if a leap second is
index c9af1ea4564b9ae6bbaa4b70061f505e2ba65f4c..24ba14d478b22a05944777f7e38fc9621d732198 100644 (file)
@@ -61,16 +61,27 @@ extern int leapsec_validate(leapsec_reader, void*);
  */
 extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on);
 
+/* Query result for a leap era. This is the minimal stateless
+ * information available for a time stamp in UTC.
+ */
+struct leap_era {
+       vint64   ebase; /* era base (UTC of start)              */
+       vint64   ttime; /* era end (UTC of next leap second)    */
+       int16_t  taiof; /* offset to TAI in this era            */
+};
+typedef struct leap_era leap_era_t;
 
 /* Query result for a leap second schedule
- * 'ttime' is the transition point in full time scale, but only if
- *     'tai_diff' is not zero. Nominal UTC time when the next leap
- *      era starts.
+ * 'ebase' is the nominal UTC time when the current leap era
+ *      started. (Era base time)
+ * 'ttime' is the next transition point in full time scale. (Nominal UTC
+ *      time when the next leap era starts.)
  * 'ddist' is the distance to the transition, in clock seconds.
  *      This is the distance to the due time, which is different
  *      from the transition time if the mode is non-electric.
  *     Only valid if 'tai_diff' is not zero.
- * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always valid.
+ * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always
+ *      valid.
  * 'tai_diff' is the change in TAI offset after the next leap
  *     transition. Zero if nothing is pending or too far ahead.
  * 'warped' is set only once, when the the leap second occurred between
@@ -81,6 +92,7 @@ extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on);
  * 'dynamic' != 0 if entry was requested by clock/peer
  */ 
 struct leap_result {
+       vint64   ebase;
        vint64   ttime;
        uint32_t ddist;
        int16_t  tai_offs;
@@ -91,6 +103,15 @@ struct leap_result {
 };
 typedef struct leap_result leap_result_t;
 
+/* The leap signature is used in two distinct circumstances, and it has
+ * slightly different content in these cases:
+ *  - it is used to indictae the time range covered by the leap second
+ *    table, and then it contains the last transition, TAI offset after
+ *    the final transition, and the expiration time.
+ *  - it is used to query data for AUTOKEY updates, and then it contains
+ *    the *current* TAI offset, the *next* transition time and the
+ *    expiration time of the table.
+ */   
 struct leap_signature {
        uint32_t etime; /* expiration time      */
        uint32_t ttime; /* transition time      */
@@ -170,6 +191,7 @@ extern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot);
  */
 extern void leapsec_reset_frame(void);
 
+#if 0 /* currently unused -- possibly revived later */
 /* Given a transition time, the TAI offset valid after that and an
  * expiration time, try to establish a system leap transition. Only
  * works if the existing table is extended. On success, updates the
@@ -177,6 +199,7 @@ extern void leapsec_reset_frame(void);
  */
 extern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime,
                                   const time_t * pivot);
+#endif
 
 /* Take a time stamp and create a leap second frame for it. This will
  * schedule a leap second for the beginning of the next month, midnight
@@ -200,9 +223,15 @@ extern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now,
  * last and the current query. In that case, qr->warped contains the
  * required clock stepping, which is always zero in electric mode.
  */
-extern int/*BOOL*/ leapsec_query(leap_result_t *qr, uint32_t ntpts,
+extern int/*BOOL*/ leapsec_query(leap_result_t * qr, uint32_t ntpts,
                                 const time_t * pivot);
 
+/* For a given time stamp, fetch the data for the bracketing leap
+ * era. The time stamp is subject to NTP era unfolding.
+ */
+extern int/*BOOL*/ leapsec_query_era(leap_era_t * qr, uint32_t ntpts,
+                                    const time_t * pivot);
+
 /* Get the current leap frame info. Returns TRUE if the result contains
  * useable data, FALSE if there is currently no leap second frame.
  * This merely replicates some results from a previous query, but since
@@ -215,6 +244,14 @@ extern int/*BOOL*/ leapsec_query(leap_result_t *qr, uint32_t ntpts,
  */
 extern int/*BOOL*/ leapsec_frame(leap_result_t *qr);
 
+
+/* Process a AUTOKEY TAI offset information. This *might* augment the
+ * current leap data table with the given TAI offset.
+ * Returns TRUE if action was taken, FALSE otherwise.
+ */
+extern int/*BOOL*/ leapsec_autokey_tai(int tai_offset, uint32_t ntpnow,
+                                      const time_t * pivot);
+
 /* reset global state for unit tests */
 extern void leapsec_ut_pristine(void);
 
index d9a68de7bb3f135bf6f71fb6b9f075f61ace8dc4..ac5f153e4ba1f0252f06f75a4e72a4d340497c6e 100644 (file)
@@ -222,9 +222,10 @@ static const char leap_gthash [] = {
     "#h        1151a8f e85a5069 9000fcdb 3d5e5365 1d505b37"
 };
 
-static const uint32_t lsec2006 = 3345062400u; // 1 Jan 2006, 00:00:00 utc
-static const uint32_t lsec2009 = 3439756800u; // 1 Jan 2009, 00:00:00 utc
-static const uint32_t lsec2012 = 3550089600u; // 1 Jul 2012, 00:00:00 utc
+static const uint32_t lsec2006 = 3345062400u; // +33, 1 Jan 2006, 00:00:00 utc
+static const uint32_t lsec2009 = 3439756800u; // +34, 1 Jan 2009, 00:00:00 utc
+static const uint32_t lsec2012 = 3550089600u; // +35, 1 Jul 2012, 00:00:00 utc
+static const uint32_t lsec2015 = 3644697600u; // +36, 1 Jul 2015, 00:00:00 utc
 
 int stringreader(void* farg)
 {
@@ -654,7 +655,7 @@ TEST_F(leapsecTest, addDynamic) {
 
        leap_table_t * pt = leapsec_get_table(0);
        for (int idx=1; insns[idx]; ++idx) {
-           rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
+               rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
                EXPECT_EQ(TRUE, rc);
        }
        // try to slip in a previous entry
@@ -665,6 +666,7 @@ TEST_F(leapsecTest, addDynamic) {
 
 // ----------------------------------------------------------------------
 // add fixed leap seconds (like from network packet)
+#if 0 /* currently unused -- possibly revived later */
 TEST_F(leapsecTest, addFixed) {
        int            rc;
        leap_result_t  qr;
@@ -711,9 +713,11 @@ TEST_F(leapsecTest, addFixed) {
        EXPECT_EQ(FALSE, rc);
        //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
 }
+#endif
 
 // ----------------------------------------------------------------------
 // add fixed leap seconds (like from network packet)
+#if 0 /* currently unused -- possibly revived later */
 TEST_F(leapsecTest, addFixedExtend) {
        int            rc;
        leap_result_t  qr;
@@ -728,7 +732,7 @@ TEST_F(leapsecTest, addFixedExtend) {
        rc = setup_load_table(leap2, FALSE);
        EXPECT_EQ(1, rc);
 
-       leap_table_t * pt = leapsec_get_table(0);
+       leap_table_t * pt = leapsec_get_table(FALSE);
        for (last=idx=0; insns[idx].tt; ++idx) {
                last = idx;
                rc = leapsec_add_fix(
@@ -756,11 +760,13 @@ TEST_F(leapsecTest, addFixedExtend) {
        EXPECT_EQ(FALSE, rc);
        //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
 }
+#endif
 
 // ----------------------------------------------------------------------
 // add fixed leap seconds (like from network packet) in an otherwise
 // empty table and test queries before / between /after the tabulated
 // values.
+#if 0 /* currently unused -- possibly revived later */
 TEST_F(leapsecTest, setFixedExtend) {
        int            rc;
        leap_result_t  qr;
@@ -797,6 +803,77 @@ TEST_F(leapsecTest, setFixedExtend) {
 
        //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
 }
+#endif
+
+// =====================================================================
+// AUTOKEY LEAP TRANSFER TESTS
+// =====================================================================
+
+// ----------------------------------------------------------------------
+// Check if the offset can be applied to an empty table ONCE
+TEST_F(leapsecTest, taiEmptyTable) {
+       int rc;
+
+       rc = leapsec_autokey_tai(35, lsec2015-30*86400, NULL);  
+       EXPECT_EQ(TRUE, rc);
+
+       rc = leapsec_autokey_tai(35, lsec2015-29*86400, NULL);
+       EXPECT_EQ(FALSE, rc);
+}
+
+// ----------------------------------------------------------------------
+// Check that with fixed entries the operation fails
+TEST_F(leapsecTest, taiTableFixed) {
+       int rc;
+
+       rc = setup_load_table(leap1, FALSE);
+       EXPECT_EQ(1, rc);
+
+       rc = leapsec_autokey_tai(35, lsec2015-30*86400, NULL);
+       EXPECT_EQ(FALSE, rc);
+}
+
+// ----------------------------------------------------------------------
+// test adjustment with a dynamic entry already there
+TEST_F(leapsecTest, taiTableDynamic) {
+       int        rc;
+       leap_era_t era;
+
+       rc = leapsec_add_dyn(TRUE, lsec2015-20*SECSPERDAY, NULL);
+       EXPECT_EQ(TRUE, rc);
+
+       leapsec_query_era(&era, lsec2015-10, NULL);
+       EXPECT_EQ(0, era.taiof);
+       leapsec_query_era(&era, lsec2015+10, NULL);
+       EXPECT_EQ(1, era.taiof);
+
+       rc = leapsec_autokey_tai(35, lsec2015-19*86400, NULL);  
+       EXPECT_EQ(TRUE, rc);
+
+       rc = leapsec_autokey_tai(35, lsec2015-19*86400, NULL);
+       EXPECT_EQ(FALSE, rc);
+
+       leapsec_query_era(&era, lsec2015-10, NULL);
+       EXPECT_EQ(35, era.taiof);
+       leapsec_query_era(&era, lsec2015+10, NULL);
+       EXPECT_EQ(36, era.taiof);
+}
+
+// ----------------------------------------------------------------------
+// test adjustment with a dynamic entry already there in dead zone
+TEST_F(leapsecTest, taiTableDynamicDeadZone) {
+       int rc;
+
+       rc = leapsec_add_dyn(TRUE, lsec2015-20*SECSPERDAY, NULL);
+       EXPECT_EQ(TRUE, rc);
+
+       rc = leapsec_autokey_tai(35, lsec2015-5, NULL); 
+       EXPECT_EQ(FALSE, rc);
+
+       rc = leapsec_autokey_tai(35, lsec2015+5, NULL);
+       EXPECT_EQ(FALSE, rc);
+}
+
 
 // =====================================================================
 // SEQUENCE TESTS