From: Juergen Perlinger Date: Sun, 17 Nov 2013 13:36:40 +0000 (+0100) Subject: [bug 2326] use time-to-live of leap second table for better diagnostics X-Git-Tag: NTP_4_2_7P396~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cce18487caa3aca8ec6c747e520f25978edc3945;p=thirdparty%2Fntp.git [bug 2326] use time-to-live of leap second table for better diagnostics bk: 5288c668LdoAGMxz9Y-YBQrHfm8WJA --- diff --git a/ntpd/ntp_leapsec.c b/ntpd/ntp_leapsec.c index 458ae5d9e..22e2bd9c7 100644 --- a/ntpd/ntp_leapsec.c +++ b/ntpd/ntp_leapsec.c @@ -100,12 +100,12 @@ strtouv64( * multiplication. Slow but portable. */ { - u_int32 accu; - accu = (u_int32)res.W_s.ll * base; - res.W_s.ll = (u_short)accu; + uint32_t accu; + accu = (uint32_t)res.W_s.ll * base; + res.W_s.ll = (uint16_t)accu; accu = (accu >> 16) - + (u_int32)res.W_s.lh * base; - res.W_s.lh = (u_short)accu; + + (uint32_t)res.W_s.lh * base; + res.W_s.lh = (uint16_t)accu; /* the upper bits can be done in one step: */ res.D_s.hi = res.D_s.hi * base + (accu >> 16); } @@ -162,10 +162,44 @@ int ucmpv64( return res; } +#if 0 +static vint64 +addv64( + const vint64 *lhs, + const vint64 *rhs) +{ + vint64 res; + +#if defined(HAVE_INT64) + res.Q_s = lhs->Q_s + rhs->Q_s; +#else + res = *lhs; + M_ADD(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo); +#endif + return res; +} +#endif + +static vint64 +subv64( + const vint64 *lhs, + const vint64 *rhs) +{ + vint64 res; + +#if defined(HAVE_INT64) + res.Q_s = lhs->Q_s - rhs->Q_s; +#else + res = *lhs; + M_SUB(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo); +#endif + return res; +} + static vint64 addv64i32( const vint64 * lhs, - int32 rhs) + int32_t rhs) { vint64 res; @@ -182,7 +216,7 @@ addv64i32( static vint64 subv64i32( const vint64 * lhs, - int32 rhs) + int32_t rhs) { vint64 res; @@ -200,7 +234,7 @@ subv64i32( static vint64 addv64u32( const vint64 * lhs, - u_int32 rhs) + uint32_t rhs) { vint64 res; @@ -217,7 +251,7 @@ addv64u32( static vint64 subv64u32( const vint64 * lhs, - u_int32 rhs) + uint32_t rhs) { vint64 res; @@ -264,24 +298,25 @@ ntpcal_date_to_ntp64( #define MAX_HIST 10 /* history of leap seconds */ struct leap_info { - vint64 ttime; /* transition time (after the step, ntp scale) */ - u_int32 stime; /* schedule limit (a month before transition) */ - short taiof; /* TAI offset on and after the transition */ - u_short dynls; /* dynamic: inserted on peer/clock request */ + vint64 ttime; /* transition time (after the step, ntp scale) */ + uint32_t stime; /* schedule limit (a month before transition) */ + int16_t taiof; /* TAI offset on and after the transition */ + uint8_t dynls; /* dynamic: inserted on peer/clock request */ }; typedef struct leap_info leap_info_t; struct leap_head { - vint64 expire; /* table expiration time */ - u_short size; /* number of infos in table */ - short base_tai; /* total leaps before first entry */ - short this_tai; /* current TAI offset */ - short next_tai; /* TAI offset after 'when' */ - vint64 dtime; /* due time (current era end) */ - vint64 ttime; /* nominal transition time (next era start) */ - vint64 stime; /* schedule time (when we take notice) */ - vint64 ebase; /* base time of this leap era */ - short dynls; /* next leap is dynamic (by peer request) */ + vint64 update; /* time of information update */ + vint64 expire; /* table expiration time */ + uint16_t size; /* number of infos in table */ + int16_t base_tai; /* total leaps before first entry */ + int16_t this_tai; /* current TAI offset */ + int16_t next_tai; /* TAI offset after 'when' */ + vint64 dtime; /* due time (current era end) */ + vint64 ttime; /* nominal transition time (next era start) */ + vint64 stime; /* schedule time (when we take notice) */ + vint64 ebase; /* base time of this leap era */ + uint8_t dynls; /* next leap is dynamic (by peer request) */ }; typedef struct leap_head leap_head_t; @@ -301,7 +336,7 @@ 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 int betweenu32(u_int32, u_int32, u_int32); +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); static int leapsec_raw(leap_table_t*, const vint64 *, int, int); @@ -373,21 +408,6 @@ leapsec_clear( reset_times(pt); } -/* --------------------------------------------------------------------- - * Check if expired at a given time - */ -int/*BOOL*/ -leapsec_is_expired( - leap_table_t * pt , - u_int32 when, - const time_t * tpiv) -{ - vint64 limit; - - limit = ntpcal_ntp_to_ntp(when, tpiv); - return ucmpv64(&limit, &pt->head.expire) >= 0; -} - /* --------------------------------------------------------------------- * Load a leap second file and check expiration on the go */ @@ -413,12 +433,17 @@ leapsec_load( cp = linebuf; if (*cp == '#') { cp++; - if (*cp == '@' || *cp == '$') { + if (*cp == '@') { cp = skipws(cp+1); pt->head.expire = strtouv64(cp, &ep, 10); if (parsefail(cp, ep)) goto fail_read; pt->lsig.etime = pt->head.expire.D_s.lo; + } else if (*cp == '$') { + cp = skipws(cp+1); + pt->head.update = strtouv64(cp, &ep, 10); + if (parsefail(cp, ep)) + goto fail_read; } } else if (isdigit((u_char)*cp)) { ttime = strtouv64(cp, &ep, 10); @@ -434,10 +459,10 @@ leapsec_load( taiof, FALSE)) goto fail_insn; } else { - pt->head.base_tai = (short)taiof; + pt->head.base_tai = (int16_t)taiof; } pt->lsig.ttime = ttime.D_s.lo; - pt->lsig.taiof = (short)taiof; + pt->lsig.taiof = (int16_t)taiof; } } return TRUE; @@ -489,12 +514,12 @@ leapsec_dump( int/*BOOL*/ leapsec_query( leap_result_t * qr , - u_int32 ts32 , + uint32_t ts32 , const time_t * pivot) { leap_table_t * pt; vint64 ts64, last, next; - u_int32 due32; + uint32_t due32; int fired; /* preset things we use later on... */ @@ -519,8 +544,8 @@ leapsec_query( * both modes is easier to maintain. */ last = pt->head.ttime; - qr->warped = (short)(last.D_s.lo - - pt->head.dtime.D_s.lo); + qr->warped = (int16_t)(last.D_s.lo - + pt->head.dtime.D_s.lo); next = addv64i32(&ts64, qr->warped); reload_limits(pt, &next); fired = ucmpv64(&pt->head.ebase, &last) == 0; @@ -615,18 +640,38 @@ leapsec_getsig( /* ------------------------------------------------------------------ */ int/*BOOL*/ leapsec_expired( - u_int32 when, + uint32_t when, const time_t * tpiv) { - return leapsec_is_expired(leapsec_get_table(FALSE), when, tpiv); + const leap_table_t * pt; + vint64 limit; + + pt = leapsec_get_table(FALSE); + limit = ntpcal_ntp_to_ntp(when, tpiv); + return ucmpv64(&limit, &pt->head.expire) >= 0; +} + +/* ------------------------------------------------------------------ */ +int32_t +leapsec_daystolive( + uint32_t when, + const time_t * tpiv) +{ + const leap_table_t * pt; + vint64 limit; + + pt = leapsec_get_table(FALSE); + limit = ntpcal_ntp_to_ntp(when, tpiv); + limit = subv64(&pt->head.expire, &limit); + return ntpcal_daysplit(&limit).hi; } /* ------------------------------------------------------------------ */ int/*BOOL*/ leapsec_add_fix( int total, - u_int32 ttime, - u_int32 etime, + uint32_t ttime, + uint32_t etime, const time_t * pivot) { time_t tpiv; @@ -648,7 +693,7 @@ leapsec_add_fix( pt->lsig.etime = etime; pt->lsig.ttime = ttime; - pt->lsig.taiof = (short)total; + pt->lsig.taiof = (int16_t)total; pt->head.expire = et64; @@ -659,7 +704,7 @@ leapsec_add_fix( int/*BOOL*/ leapsec_add_dyn( int insert, - u_int32 ntpnow, + uint32_t ntpnow, const time_t * pivot ) { leap_table_t * pt; @@ -930,7 +975,7 @@ leapsec_raw( stime = ntpcal_date_to_ntp64(&fts); li.ttime = *ttime; li.stime = ttime->D_s.lo - stime.D_s.lo; - li.taiof = (short)taiof; + li.taiof = (int16_t)taiof; li.dynls = (dynls != 0); return add_range(pt, &li); } @@ -941,9 +986,9 @@ leapsec_raw( */ static int/*BOOL*/ betweenu32( - u_int32 lo, - u_int32 x, - u_int32 hi) + uint32_t lo, + uint32_t x, + uint32_t hi) { int rc; if (lo <= hi) diff --git a/ntpd/ntp_leapsec.h b/ntpd/ntp_leapsec.h index bea02d990..f2442c224 100644 --- a/ntpd/ntp_leapsec.h +++ b/ntpd/ntp_leapsec.h @@ -58,7 +58,7 @@ extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on); * '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' not zero. + * Only valid if 'tai_diff' is not zero. * '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. @@ -70,20 +70,20 @@ extern int/*BOOL*/ leapsec_electric(int/*BOOL*/ on); * 'dynamic' != 0 if entry was requested by clock/peer */ struct leap_result { - vint64 ttime; - u_int32 ddist; - short tai_offs; - short tai_diff; - short warped; - u_short proximity; - short dynamic; + vint64 ttime; + uint32_t ddist; + int16_t tai_offs; + int16_t tai_diff; + int16_t warped; + uint8_t proximity; + uint8_t dynamic; }; typedef struct leap_result leap_result_t; struct leap_signature { - u_int32 etime; /* expiration time */ - u_int32 ttime; /* transition time */ - short taiof; /* total offset to TAI */ + uint32_t etime; /* expiration time */ + uint32_t ttime; /* transition time */ + int16_t taiof; /* total offset to TAI */ }; typedef struct leap_signature leap_signature_t; @@ -93,7 +93,7 @@ typedef struct leap_signature leap_signature_t; #define LSPROX_ANNOUNCE 2 /* less than 1 day to target */ #define LSPROX_ALERT 3 /* less than 10 sec to target */ -/* Get the current or alternate table ponter. Getting the alternate +/* Get the current or alternate table pointer. Getting the alternate * pointer will automatically copy the primary table, so it can be * subsequently modified. */ @@ -108,12 +108,6 @@ extern int/*BOOL*/ leapsec_set_table(leap_table_t *); /* Clear all leap second data. Use it for init & cleanup */ extern void leapsec_clear(leap_table_t*); -/* Check if a table is expired at a given point in time. 'when' is - * subject to NTP era unfolding. - */ -extern int/*BOOL*/ leapsec_is_expired(leap_table_t*, u_int32 when, - const time_t * pivot); - /* Load a leap second file. If 'blimit' is set, do not store (but * register with their TAI offset) leap entries before the build date. * Update the leap signature data on the fly. @@ -140,7 +134,13 @@ extern void leapsec_getsig(leap_signature_t * psig); /* Check if the leap table is expired at the given time. */ -extern int/*BOOL*/ leapsec_expired(u_int32 when, const time_t * pivot); +extern int/*BOOL*/ leapsec_expired(uint32_t when, const time_t * pivot); + +/* Get the distance to expiration in days. + * Returns negative values if expired, zero if there are less than 24hrs + * left, and positive numbers otherwise. + */ +extern int32_t leapsec_daystolive(uint32_t when, const time_t * pivot); /* Reset the current leap frame, so the next query will do proper table * lookup from fresh. Suppresses a possible leap era transition detection @@ -153,7 +153,7 @@ extern void leapsec_reset_frame(void); * works if the existing table is extended. On success, updates the * signature data. */ -extern int/*BOOL*/ leapsec_add_fix(int offset, u_int32 ttime, u_int32 etime, +extern int/*BOOL*/ leapsec_add_fix(int offset, uint32_t ttime, uint32_t etime, const time_t * pivot); /* Take a time stamp and create a leap second frame for it. This will @@ -168,7 +168,7 @@ extern int/*BOOL*/ leapsec_add_fix(int offset, u_int32 ttime, u_int32 etime, * 'ntp_now' is subject to era unfolding. The entry is marked * dynamic. The leap signature is NOT updated. */ -extern int/*BOOL*/ leapsec_add_dyn(int insert, u_int32 ntp_now, +extern int/*BOOL*/ leapsec_add_dyn(int/*BOOL*/ insert, uint32_t ntp_now, const time_t * pivot); /* Take a time stamp and get the associated leap information. The time @@ -178,7 +178,7 @@ extern int/*BOOL*/ leapsec_add_dyn(int insert, u_int32 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, u_int32 ntpts, +extern int/*BOOL*/ leapsec_query(leap_result_t *qr, uint32_t ntpts, const time_t * pivot); /* Get the current leap frame info. Returns TRUE if the result contains diff --git a/ntpd/ntp_util.c b/ntpd/ntp_util.c index 9f538c1de..185f4b0e7 100644 --- a/ntpd/ntp_util.c +++ b/ntpd/ntp_util.c @@ -864,8 +864,8 @@ record_timing_stats( * * Returns: * -1 if there was a problem, - * 0 if the leapfile has expired - * >0 # of days until the leapfile expires + * 0 if the leapfile has expired or less than 24hrs remaining TTL + * >0 # of full days until the leapfile expires */ int check_leap_file( @@ -875,37 +875,39 @@ check_leap_file( FILE *fp; struct stat *sp1 = &leapseconds_file_sb1; struct stat *sp2 = &leapseconds_file_sb2; - int rc; + int rc = INT_MAX; /* assume not expired for long a time */ + l_fp now; + if (leapseconds_file) { + get_systime(&now); if ((fp = fopen(leapseconds_file, "r")) == NULL) { msyslog(LOG_ERR, - "check_leap_file: fopen(%s): %m", - leapseconds_file); - return -1; - } - if (fstat(fileno(fp), &leapseconds_file_sb2)) { + "check_leap_file: fopen(%s): %m", + leapseconds_file); + rc = -1; + } else if (fstat(fileno(fp), &leapseconds_file_sb2)) { msyslog(LOG_ERR, - "check_leap_file: stat(%s): %m", - leapseconds_file); - fclose(fp); - return -1; - } - if ( (sp1->st_mtime != sp2->st_mtime) - || (sp1->st_ctime != sp2->st_ctime)) { + "check_leap_file: stat(%s): %m", + leapseconds_file); + rc = -1; + } else if ( (sp1->st_mtime != sp2->st_mtime) + || (sp1->st_ctime != sp2->st_ctime)) { leapseconds_file_sb1 = leapseconds_file_sb2; if (!leapsec_load_file(fp, TRUE)) { msyslog(LOG_ERR, - "format error leapseconds file %s", - leapseconds_file); + "format error leapseconds file %s", + leapseconds_file); rc = -1; - } else { - rc = 1; /* XXX: 0 or days til expire */ } - } else { - rc = 0; /* XXX: 0 or days til expire */ } - fclose(fp); + if (rc >= 0) { + rc = leapsec_daystolive(now.l_ui, NULL); + if (rc < 0) + rc = 0; + } + if (fp != NULL) + fclose(fp); } return rc; diff --git a/tests/ntpd/leapsec.cpp b/tests/ntpd/leapsec.cpp index e8abd28cb..2fc528e7e 100644 --- a/tests/ntpd/leapsec.cpp +++ b/tests/ntpd/leapsec.cpp @@ -256,7 +256,7 @@ TEST_F(leapsecTest, tableSelect) { EXPECT_NE(pt2, pt3); } -// load file & checl expiration +// load file & check expiration TEST_F(leapsecTest, loadFileExpire) { const char *cp = leap1; int rc; @@ -271,6 +271,33 @@ TEST_F(leapsecTest, loadFileExpire) { EXPECT_EQ(1, rc); } +// load file & check time-to-live +TEST_F(leapsecTest, loadFileTTL) { + const char *cp = leap1; + int rc; + leap_table_t * pt = leapsec_get_table(0); + time_t pivot = 0x70000000; + + const uint32_t limit = 3610569600u; + + rc = leapsec_load(pt, stringreader, &cp, FALSE) + && leapsec_set_table(pt); + ASSERT_EQ(1, rc); + + // exactly 1 day to live + rc = leapsec_daystolive(limit - 86400, &pivot); + EXPECT_EQ( 1, rc); + // less than 1 day to live + rc = leapsec_daystolive(limit - 86399, &pivot); + EXPECT_EQ( 0, rc); + // hit expiration exactly + rc = leapsec_daystolive(limit, &pivot); + EXPECT_EQ( 0, rc); + // expired since 1 sec + rc = leapsec_daystolive(limit + 1, &pivot); + EXPECT_EQ(-1, rc); +} + // ad-hoc jump: leap second at 2009.01.01 -60days TEST_F(leapsecTest, ls2009faraway) { int rc;