Skip to content

Commit 51c94ed

Browse files
committed
Refactor NTP code
1 parent bac5e26 commit 51c94ed

6 files changed

Lines changed: 373 additions & 360 deletions

File tree

ntp.go

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package ntp
1212

1313
import (
1414
"bytes"
15+
"crypto/rand"
1516
"encoding/binary"
1617
"errors"
1718
"fmt"
@@ -266,15 +267,15 @@ type Response struct {
266267
Stratum uint8
267268

268269
// Timescale reports the timescale used by the server to timestamp
269-
// messages it sent and received. Used only in NTPv5.
270+
// messages it sent and received. Always TimescaleUTC for NTPv3 and NTPv4.
270271
Timescale Timescale
271272

272273
// TimescaleOffsets contains the time offsets of any requested additional
273274
// timescales relative to the primary timescale. Used only in NTPv5.
274275
TimescaleOffsets []TimescaleOffset
275276

276277
// Era is the NTP era number returned by the server. Era 0 spans
277-
// 1900-2036, Era 1 spans 2036-2172, etc. Used only in NTPv5.
278+
// 1900-2036, Era 1 spans 2036-2172, etc.
278279
Era uint8
279280

280281
// ReferenceID is a 32-bit integer used to help identify which server or
@@ -716,6 +717,62 @@ func getEra(t time.Time) int64 {
716717
return int64(ntpSec >> 32)
717718
}
718719

720+
// offset calculates an uncorrected clock offset using the four NTP
721+
// timestamps.
722+
func offset(t1, t2, t3, t4 time.Time) time.Duration {
723+
return (t2.Sub(t1) + t3.Sub(t4)) / 2
724+
}
725+
726+
// rtt calculates the round-trip-time delay using the four NTP timestamps.
727+
func rtt(t1, t2, t3, t4 time.Time) time.Duration {
728+
return max(t4.Sub(t1)-t3.Sub(t2), 0)
729+
}
730+
731+
// minError calculates a lower bound on the error between the client and
732+
// server clocks using the four NTP timestamps.
733+
func minError(t1, t2, t3, t4 time.Time) time.Duration {
734+
if t2.Before(t1) || t4.Before(t3) {
735+
return max(t1.Sub(t2), t3.Sub(t4))
736+
}
737+
return 0
738+
}
739+
740+
// rootDistance estimates the root distance between the client and the stratum
741+
// 1 server.
742+
func rootDistance(rtt, rootDelay, rootDisp time.Duration) time.Duration {
743+
// The root distance is:
744+
// the maximum error due to all causes of the local clock
745+
// relative to the primary server. It is defined as half the
746+
// total delay plus total dispersion plus peer jitter.
747+
// (https://tools.ietf.org/html/rfc5905#appendix-A.5.5.2)
748+
//
749+
// In the reference implementation, it is calculated as follows:
750+
// rootDist = max(MINDISP, rootDelay + rtt)/2 + rootDisp
751+
// + peerDisp + PHI * (uptime - peerUptime)
752+
// + peerJitter
753+
// For an SNTP client which sends only a single packet, most of these
754+
// terms are irrelevant and become 0.
755+
totalDelay := rtt + rootDelay
756+
return totalDelay/2 + rootDisp
757+
}
758+
759+
// randUint64 creates a random non-zero 64-bit cookie value.
760+
func randUint64() (uint64, error) {
761+
for range 4 {
762+
var cookieBytes [8]byte
763+
_, err := rand.Read(cookieBytes[:])
764+
if err != nil {
765+
return 0, err
766+
}
767+
768+
cookie := binary.BigEndian.Uint64(cookieBytes[:])
769+
if cookie != 0 {
770+
return cookie, nil
771+
}
772+
}
773+
return 0, errors.New("failed to generate non-zero random value")
774+
}
775+
719776
// An timestamp is a 64-bit fixed-point (Q32.32) representation of the
720777
// number of seconds elapsed.
721778
type timestamp uint64
@@ -740,15 +797,24 @@ func (t timestamp) Time(era uint8) time.Time {
740797
// TimeV4 interprets the NTPv3/NTPv4 timestamp value as an absolute time and
741798
// returns the corresponding time.Time value.
742799
func (t timestamp) TimeV4() time.Time {
800+
if inferEra(t) == 1 {
801+
return ntpEra1.Add(t.Duration())
802+
}
803+
return ntpEra0.Add(t.Duration())
804+
}
805+
806+
// inferEra infers an NTP era from a timestamp value. This function is used
807+
// only for NTPv3 and NTPv4 timestamps.
808+
func inferEra(t timestamp) uint8 {
743809
// Assume NTP era 1 (year 2036+) if the raw timestamp suggests a year
744-
// before 1970. Otherwise assume NTP era 0. This allows the function to
745-
// report an accurate time value both before and after the 0-to-1 era
746-
// rollover.
810+
// before 1970. Otherwise assume NTP era 0. This allows us to properly
811+
// handle the rollover from era 0 to 1 when converting timestamps to
812+
// times.
747813
const t1970 = 0x83aa_7e80_0000_0000
748814
if uint64(t) < t1970 {
749-
return ntpEra1.Add(t.Duration())
815+
return 1
750816
}
751-
return ntpEra0.Add(t.Duration())
817+
return 0
752818
}
753819

754820
// toTimestamp converts a Time value into its 64-bit fixed-point timestamp

0 commit comments

Comments
 (0)