From 2fa9cd85302c53557417ac13c51fb78966fae466 Mon Sep 17 00:00:00 2001 From: Daniel Elstner Date: Mon, 27 Mar 2017 00:01:04 +0200 Subject: [PATCH] SNTP: Streamline timestamp handling Generalize the NTP timestamp conversion arithmetic, and provide hooks for using native NTP timestamps when setting or getting the system clock time. Convert microseconds to a fraction as needed when getting the system time. --- src/apps/sntp/sntp.c | 124 ++++++++++++++++++------------ src/include/lwip/apps/sntp_opts.h | 8 +- 2 files changed, 81 insertions(+), 51 deletions(-) diff --git a/src/apps/sntp/sntp.c b/src/apps/sntp/sntp.c index 71b2abed..476645c1 100644 --- a/src/apps/sntp/sntp.c +++ b/src/apps/sntp/sntp.c @@ -76,17 +76,6 @@ #error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!" #endif -/* Configure behaviour depending on microsecond or second precision */ -#ifdef SNTP_SET_SYSTEM_TIME_US -#define SNTP_CALC_TIME_US 1 -#define SNTP_RECEIVE_TIME_SIZE 2 -#else -#define SNTP_SET_SYSTEM_TIME_US(sec, us) -#define SNTP_CALC_TIME_US 0 -#define SNTP_RECEIVE_TIME_SIZE 1 -#endif - - /* the various debug levels for this file */ #define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) #define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) @@ -121,18 +110,55 @@ #define SNTP_OFFSET_RECEIVE_TIME 32 #define SNTP_OFFSET_TRANSMIT_TIME 40 -/* number of seconds between 1900 and 1970 (MSB=1)*/ -#define DIFF_SEC_1900_1970 (2208988800UL) -/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */ -#define DIFF_SEC_1970_2036 (2085978496UL) +/* Number of seconds between 1970 and Feb 7, 2036 06:28:16 UTC (epoch 1) */ +#define DIFF_SEC_1970_2036 ((u32_t)2085978496L) + +/** Convert NTP timestamp fraction to microseconds. + */ +#ifndef SNTP_FRAC_TO_US +# if LWIP_HAVE_INT64 +# define SNTP_FRAC_TO_US(f) ((u32_t)(((u64_t)(f) * 1000000UL) >> 32)) +# else +# define SNTP_FRAC_TO_US(f) ((u32_t)(f) / 4295) +# endif +#endif /* !SNTP_FRAC_TO_US */ + +/* Configure behaviour depending on native, microsecond or second precision. + * Treat NTP timestamps as signed two's-complement integers. This way, + * timestamps that have the MSB set simply become negative offsets from + * the epoch (Feb 7, 2036 06:28:16 UTC). Representable dates range from + * 1968 to 2104. + */ +#ifndef SNTP_SET_SYSTEM_TIME_NTP +# ifdef SNTP_SET_SYSTEM_TIME_US +# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \ + SNTP_SET_SYSTEM_TIME_US((u32_t)((s) + DIFF_SEC_1970_2036), SNTP_FRAC_TO_US(f)) +# else +# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \ + SNTP_SET_SYSTEM_TIME((u32_t)((s) + DIFF_SEC_1970_2036)) +# endif +#endif /* !SNTP_SET_SYSTEM_TIME_NTP */ + +/* Get the system time either natively as NTP timestamp or convert from + * Unix time in seconds and microseconds. Take care to avoid overflow if the + * microsecond value is at the maximum of 999999. Also add 0.5 us fudge to + * avoid special values like 0, and to mask round-off errors that would + * otherwise break round-trip conversion identity. + */ +#ifndef SNTP_GET_SYSTEM_TIME_NTP +# define SNTP_GET_SYSTEM_TIME_NTP(s, f) do { \ + u32_t sec_, usec_; \ + SNTP_GET_SYSTEM_TIME(sec_, usec_); \ + (s) = (s32_t)(sec_ - DIFF_SEC_1970_2036); \ + (f) = usec_ * 4295 - ((usec_ * 2143) >> 16) + 2147; \ + } while (0) +#endif /* !SNTP_GET_SYSTEM_TIME_NTP */ /** * SNTP packet format (without optional fields) * Timestamps are coded as 64 bits: - * - 32 bits seconds since Jan 01, 1970, 00:00 - * - 32 bits seconds fraction (0-padded) - * For future use, if the MSB in the seconds part is set, seconds are based - * on Feb 07, 2036, 06:28:16. + * - signed 32 bits seconds since Feb 07, 2036, 06:28:16 UTC (epoch 1) + * - unsigned 32 bits seconds fraction (2^32 = 1 second) */ #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" @@ -203,34 +229,32 @@ static ip_addr_t sntp_last_server_address; static u32_t sntp_last_timestamp_sent[2]; #endif /* SNTP_CHECK_RESPONSE >= 2 */ +#ifndef sntp_format_time +/* Debug print helper. */ +static const char * +sntp_format_time(s32_t sec) +{ + time_t ut; + ut = (u32_t)((u32_t)sec + DIFF_SEC_1970_2036); + return ctime(&ut); +} +#endif /* !sntp_format_time */ + /** * SNTP processing of received timestamp */ static void sntp_process(u32_t *receive_timestamp) { - /* convert SNTP time (1900-based) to unix GMT time (1970-based) - * if MSB is 0, SNTP time is 2036-based! - */ - u32_t rx_secs = lwip_ntohl(receive_timestamp[0]); - int is_1900_based = ((rx_secs & 0x80000000) != 0); - u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036); - time_t tim = t; + s32_t sec; + u32_t frac; -#if SNTP_CALC_TIME_US - u32_t us = lwip_ntohl(receive_timestamp[1]) / 4295; - SNTP_SET_SYSTEM_TIME_US(t, us); - /* display local time from GMT time */ - LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us)); + sec = lwip_ntohl(receive_timestamp[0]); + frac = lwip_ntohl(receive_timestamp[1]); -#else /* SNTP_CALC_TIME_US */ - - /* change system time and/or the update the RTC clock */ - SNTP_SET_SYSTEM_TIME(t); - /* display local time from GMT time */ - LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim))); -#endif /* SNTP_CALC_TIME_US */ - LWIP_UNUSED_ARG(tim); + SNTP_SET_SYSTEM_TIME_NTP(sec, frac); + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %" U32_F " us\n", + sntp_format_time(sec), SNTP_FRAC_TO_US(frac))); } /** @@ -244,14 +268,16 @@ sntp_initialize_request(struct sntp_msg *req) #if SNTP_CHECK_RESPONSE >= 2 { - u32_t sntp_time_sec, sntp_time_us; - /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */ - SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us); - sntp_last_timestamp_sent[0] = lwip_htonl(sntp_time_sec + DIFF_SEC_1900_1970); - req->transmit_timestamp[0] = sntp_last_timestamp_sent[0]; - /* we send/save us instead of fraction to be faster... */ - sntp_last_timestamp_sent[1] = lwip_htonl(sntp_time_us); - req->transmit_timestamp[1] = sntp_last_timestamp_sent[1]; + u32_t sec, frac; + /* Get the transmit timestamp */ + SNTP_GET_SYSTEM_TIME_NTP(sec, frac); + sec = lwip_htonl(sec); + frac = lwip_htonl(frac); + + sntp_last_timestamp_sent[0] = sec; + sntp_last_timestamp_sent[1] = frac; + req->transmit_timestamp[0] = sec; + req->transmit_timestamp[1] = frac; } #endif /* SNTP_CHECK_RESPONSE >= 2 */ } @@ -336,7 +362,7 @@ sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, { u8_t mode; u8_t stratum; - u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE]; + u32_t receive_timestamp[2]; err_t err; LWIP_UNUSED_ARG(arg); @@ -383,7 +409,7 @@ sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, { /* correct answer */ err = ERR_OK; - pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME); + pbuf_copy_partial(p, &receive_timestamp, 8, SNTP_OFFSET_TRANSMIT_TIME); } } } else { diff --git a/src/include/lwip/apps/sntp_opts.h b/src/include/lwip/apps/sntp_opts.h index f3651f90..c3a457e7 100644 --- a/src/include/lwip/apps/sntp_opts.h +++ b/src/include/lwip/apps/sntp_opts.h @@ -46,8 +46,10 @@ */ /** SNTP macro to change system time in seconds - * Define SNTP_SET_SYSTEM_TIME_US(sec, us) to set the time in microseconds instead of this one - * if you need the additional precision. + * Define SNTP_SET_SYSTEM_TIME_US(sec, us) to set the time in microseconds + * instead of this one if you need the additional precision. Alternatively, + * define SNTP_SET_SYSTEM_TIME_NTP(sec, frac) in order to work with native + * NTP timestamps instead. */ #if !defined SNTP_SET_SYSTEM_TIME || defined __DOXYGEN__ #define SNTP_SET_SYSTEM_TIME(sec) LWIP_UNUSED_ARG(sec) @@ -141,6 +143,8 @@ /** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2 * to send in request and compare in response. + * Alternatively, define SNTP_GET_SYSTEM_TIME_NTP(sec, frac) in order to + * work with native NTP timestamps instead. */ #if !defined SNTP_GET_SYSTEM_TIME || defined __DOXYGEN__ #define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0)