From 27db8a43cec224f04a057a2cb65addc7e8ebffa5 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 10 Jun 2024 17:12:09 +0200 Subject: [PATCH] Copy root delay/dispersion errors from upstream server (after first upstream NTP synchronization) Signed-off-by: DL6ER --- src/ntp/client.c | 46 ++++++++++++++++++++++++++++++++++++---------- src/ntp/ntp.h | 4 ++++ src/ntp/server.c | 26 ++++++++++++++------------ 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/src/ntp/client.c b/src/ntp/client.c index 4f253cd2..dc3db113 100644 --- a/src/ntp/client.c +++ b/src/ntp/client.c @@ -122,24 +122,29 @@ void print_debug_time(const char *label, const uint32_t *u32p, const uint64_t nt (timevar >> 32) & 0xFFFFFFFF, timevar & 0xFFFFFFFF, time_str); } -static bool settime_step(const double offset) +static uint64_t get_new_time(struct timeval *unix_time, const double offset) { // Get current time - struct timeval unix_time; - gettimeofday(&unix_time, NULL); + gettimeofday(unix_time, NULL); // Convert from double to native format (signed) and add to the // current time. Note the addition is done in native format to // avoid overflow or loss of precision. - const uint64_t ntp_time = U2LFP(unix_time) + D2LFP(offset); + const uint64_t ntp_time = U2LFP(*unix_time) + D2LFP(offset); // Convert NTP to native format - unix_time.tv_sec = NTPtoSEC(ntp_time); - unix_time.tv_usec = NTPtoUSEC(ntp_time); + unix_time->tv_sec = NTPtoSEC(ntp_time); + unix_time->tv_usec = NTPtoUSEC(ntp_time); + + return ntp_time; +} + +static bool settime_step(struct timeval *unix_time, const double offset) +{ log_debug(DEBUG_NTP, "Stepping system time by %e s", offset); // Set time immediately - if(settimeofday(&unix_time, NULL) != 0) + if(settimeofday(unix_time, NULL) != 0) { log_err("Failed to set time: %s", errno == EPERM ? "Insufficient permissions, try running with sudo" : strerror(errno)); @@ -221,9 +226,16 @@ static bool reply(int fd, struct ntp_sync *ntp, const bool verbose) // Compute precision of server clock in seconds 2^rho ntp->precision = pow(2, rho); + // Get root delay and dispersion (in network-byte-order !) + memcpy(&ntp_root_delay, &buf[4], sizeof(ntp_root_delay)); + memcpy(&ntp_root_dispersion, &buf[8], sizeof(ntp_root_dispersion)); + // Extract Transmit Timestamp - // org = Origin Timestamp (Transmit Timestamp @ Client) uint64_t netbuffer; + // ref = Reference Timestamp (Time at which the clock was last set or corrected) + memcpy(&netbuffer, &buf[16], sizeof(netbuffer)); + const uint64_t ref = ntoh64(netbuffer); + // org = Origin Timestamp (Transmit Timestamp @ Client) memcpy(&netbuffer, &buf[24], sizeof(netbuffer)); const uint64_t org = ntoh64(netbuffer); // rec = Receive Timestamp (Receive Timestamp @ Server) @@ -287,6 +299,9 @@ static bool reply(int fd, struct ntp_sync *ntp, const bool verbose) if(!config.debug.ntp.v.b) return true; + // Print current time at server + print_debug_time("Server reference time", NULL, ref); + // Print current time at client print_debug_time("Current time at client", NULL, dst); @@ -296,6 +311,10 @@ static bool reply(int fd, struct ntp_sync *ntp, const bool verbose) // Print offset and delay log_debug(DEBUG_NTP, "Time offset: %e s", ntp->theta); log_debug(DEBUG_NTP, "Round-trip delay: %e s", ntp->delta); + const uint32_t root_delay = ntohl(ntp_root_delay); + log_debug(DEBUG_NTP, "Root delay: %e s", LFP2D(root_delay)); + const uint32_t root_dispersion = ntohl(ntp_root_dispersion); + log_debug(DEBUG_NTP, "Root dispersion: %e s", LFP2D(root_dispersion)); return true; } @@ -398,7 +417,7 @@ bool ntp_client(const char *server, const bool settime, const bool print) free(ntp); return false; } - log_info("Received %u/%u valid NTP replies", valid, count); + log_info("Received %u/%u valid NTP replies from %s", valid, count, server); theta_avg /= valid; delta_avg /= valid; @@ -466,6 +485,10 @@ bool ntp_client(const char *server, const bool settime, const bool print) // Set time if requested if(settime) { + // Calculate corrected time + struct timeval unix_time; + const uint64_t ntp_time = get_new_time(&unix_time, theta_trim); + // If the clock deviates more than 0.5 seconds from the NTP server, // the time is updated immediately. Otherwise, the time is updated // gradually to avoid sudden jumps in the system clock. @@ -473,7 +496,7 @@ bool ntp_client(const char *server, const bool settime, const bool print) // since Linux 2.6.26, see man ntp_adjtime(2) for details. bool success; if(fabs(theta_trim) > 0.5) - success = settime_step(theta_trim); + success = settime_step(&unix_time, theta_trim); else success = settime_skew(theta_trim); @@ -481,6 +504,9 @@ bool ntp_client(const char *server, const bool settime, const bool print) if(!success) return false; + // Update last NTP sync time + ntp_last_sync = ntp_time; + // Finally, adjust RTC if configured if(config.ntp.rtc.set.v.b) ntp_sync_rtc(); diff --git a/src/ntp/ntp.h b/src/ntp/ntp.h index b87a7878..d8dbf451 100644 --- a/src/ntp/ntp.h +++ b/src/ntp/ntp.h @@ -65,6 +65,10 @@ bool ntp_sync_rtc(void); #define hton64(x) ((((uint64_t)htonl(x)) << 32) + htonl((x) >> 32)) #define ntoh64(x) ((((uint64_t)ntohl(x)) << 32) + ntohl((x) >> 32)) +extern uint64_t ntp_last_sync; +extern uint32_t ntp_root_delay; +extern uint32_t ntp_root_dispersion; + #endif // NTP_H diff --git a/src/ntp/server.c b/src/ntp/server.c index 294ff005..048a4bdf 100644 --- a/src/ntp/server.c +++ b/src/ntp/server.c @@ -38,6 +38,10 @@ // PRIi64 #include +uint64_t ntp_last_sync = 0u; +uint32_t ntp_root_delay = 0u; +uint32_t ntp_root_dispersion = 0u; + // RFC 5905 Appendix A.4: Kernel System Clock Interface uint64_t gettime64(void) { @@ -100,11 +104,12 @@ static bool ntp_reply(const int socket_fd, const struct sockaddr *saddr_p, const // | Root Dispersion | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // Assume Root Delay (total roundtrip delay to the primary reference - // source) = 0, Root Dispersion (the nominal error relative to the - // primary reference source) = 0 as we don't have these numbers - *u32p++ = 0.0; - *u32p++ = 0.0; + // Set Root Delay (total roundtrip delay to the primary reference + // source) and Root Dispersion (the nominal error relative to the + // primary reference source) to the values obtained from the upstream + // NTP server. These values are already in network byte order. + *u32p++ = ntp_root_delay; + *u32p++ = ntp_root_dispersion; // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -115,7 +120,7 @@ static bool ntp_reply(const int socket_fd, const struct sockaddr *saddr_p, const // Reference ID = 'LOCL" (LOCAL CLOCK) // A four-octet, left-justified, zero-padded ASCII string assigned to // the reference clock - memcpy(u32p++, "LOCL", 4); + memcpy(u32p++, "LOCL", sizeof(uint32_t)); // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -126,12 +131,9 @@ static bool ntp_reply(const int socket_fd, const struct sockaddr *saddr_p, const // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Time when the system clock was last set or corrected, in NTP - // timestamp format. As this is not a stratum 1 server, we don't have - // a hardware clock to set this value. - // A stateless server copies T3 and T4 from the client packet to T1 and - // T2 of the server packet and tacks on the transmit timestamp T3 before - // sending it to the client. - memcpy(u32p, &u32r[8], sizeof(uint64_t)); + // timestamp format. + const uint64_t last_sync = hton64(ntp_last_sync); + memcpy(u32p, &last_sync, sizeof(uint64_t)); if(config.debug.ntp.v.b) print_debug_time("Reference Timestamp", u32p, 0); u32p += 2;