github.com/sagernet/sing@v0.4.0-beta.19.0.20240518125136-f67a0988a636/common/ntp/message.go (about) 1 // Copyright 2015-2017 Brett Vickers. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package ntp provides an implementation of a Simple NTP (SNTP) client 6 // capable of querying the current time from a remote NTP server. See 7 // RFC5905 (https://tools.ietf.org/html/rfc5905) for more details. 8 // 9 // This approach grew out of a go-nuts post by Michael Hofmann: 10 // https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ 11 package ntp 12 13 import ( 14 "errors" 15 "fmt" 16 "time" 17 ) 18 19 // The LeapIndicator is used to warn if a leap second should be inserted 20 // or deleted in the last minute of the current month. 21 type LeapIndicator uint8 22 23 const ( 24 // LeapNoWarning indicates no impending leap second. 25 LeapNoWarning LeapIndicator = 0 26 27 // LeapAddSecond indicates the last minute of the day has 61 seconds. 28 LeapAddSecond = 1 29 30 // LeapDelSecond indicates the last minute of the day has 59 seconds. 31 LeapDelSecond = 2 32 33 // LeapNotInSync indicates an unsynchronized leap second. 34 LeapNotInSync = 3 35 ) 36 37 // Internal constants 38 const ( 39 defaultNtpVersion = 4 40 nanoPerSec = 1000000000 41 maxStratum = 16 42 defaultTimeout = 5 * time.Second 43 maxPollInterval = (1 << 17) * time.Second 44 maxDispersion = 16 * time.Second 45 ) 46 47 // Internal variables 48 var ( 49 ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC) 50 ) 51 52 type mode uint8 53 54 // NTP modes. This package uses only client mode. 55 const ( 56 reserved mode = 0 + iota 57 symmetricActive 58 symmetricPassive 59 client 60 server 61 broadcast 62 controlMessage 63 reservedPrivate 64 ) 65 66 // An ntpTime is a 64-bit fixed-point (Q32.32) representation of the number of 67 // seconds elapsed. 68 type ntpTime uint64 69 70 // Duration interprets the fixed-point ntpTime as a number of elapsed seconds 71 // and returns the corresponding time.Duration value. 72 func (t ntpTime) Duration() time.Duration { 73 sec := (t >> 32) * nanoPerSec 74 frac := (t & 0xffffffff) * nanoPerSec 75 nsec := frac >> 32 76 if uint32(frac) >= 0x80000000 { 77 nsec++ 78 } 79 return time.Duration(sec + nsec) 80 } 81 82 // Time interprets the fixed-point ntpTime as an absolute time and returns 83 // the corresponding time.Time value. 84 func (t ntpTime) Time() time.Time { 85 return ntpEpoch.Add(t.Duration()) 86 } 87 88 // toNtpTime converts the time.Time value t into its 64-bit fixed-point 89 // ntpTime representation. 90 func toNtpTime(t time.Time) ntpTime { 91 nsec := uint64(t.Sub(ntpEpoch)) 92 sec := nsec / nanoPerSec 93 nsec = uint64(nsec-sec*nanoPerSec) << 32 94 frac := uint64(nsec / nanoPerSec) 95 if nsec%nanoPerSec >= nanoPerSec/2 { 96 frac++ 97 } 98 return ntpTime(sec<<32 | frac) 99 } 100 101 // An ntpTimeShort is a 32-bit fixed-point (Q16.16) representation of the 102 // number of seconds elapsed. 103 type ntpTimeShort uint32 104 105 // Duration interprets the fixed-point ntpTimeShort as a number of elapsed 106 // seconds and returns the corresponding time.Duration value. 107 func (t ntpTimeShort) Duration() time.Duration { 108 sec := uint64(t>>16) * nanoPerSec 109 frac := uint64(t&0xffff) * nanoPerSec 110 nsec := frac >> 16 111 if uint16(frac) >= 0x8000 { 112 nsec++ 113 } 114 return time.Duration(sec + nsec) 115 } 116 117 // msg is an internal representation of an NTP packet. 118 type msg struct { 119 LiVnMode uint8 // Leap Indicator (2) + Version (3) + Mode (3) 120 Stratum uint8 121 Poll int8 122 Precision int8 123 RootDelay ntpTimeShort 124 RootDispersion ntpTimeShort 125 ReferenceID uint32 126 ReferenceTime ntpTime 127 OriginTime ntpTime 128 ReceiveTime ntpTime 129 TransmitTime ntpTime 130 } 131 132 // setVersion sets the NTP protocol version on the message. 133 func (m *msg) setVersion(v int) { 134 m.LiVnMode = (m.LiVnMode & 0xc7) | uint8(v)<<3 135 } 136 137 // setMode sets the NTP protocol mode on the message. 138 func (m *msg) setMode(md mode) { 139 m.LiVnMode = (m.LiVnMode & 0xf8) | uint8(md) 140 } 141 142 // setLeap modifies the leap indicator on the message. 143 func (m *msg) setLeap(li LeapIndicator) { 144 m.LiVnMode = (m.LiVnMode & 0x3f) | uint8(li)<<6 145 } 146 147 // getVersion returns the version value in the message. 148 func (m *msg) getVersion() int { 149 return int((m.LiVnMode >> 3) & 0x07) 150 } 151 152 // getMode returns the mode value in the message. 153 func (m *msg) getMode() mode { 154 return mode(m.LiVnMode & 0x07) 155 } 156 157 // getLeap returns the leap indicator on the message. 158 func (m *msg) getLeap() LeapIndicator { 159 return LeapIndicator((m.LiVnMode >> 6) & 0x03) 160 } 161 162 // A Response contains time data, some of which is returned by the NTP server 163 // and some of which is calculated by the client. 164 type Response struct { 165 // Time is the transmit time reported by the server just before it 166 // responded to the client's NTP query. 167 Time time.Time 168 169 // ClockOffset is the estimated offset of the client clock relative to 170 // the server. Add this to the client's system clock time to obtain a 171 // more accurate time. 172 ClockOffset time.Duration 173 174 // RTT is the measured round-trip-time delay estimate between the client 175 // and the server. 176 RTT time.Duration 177 178 // Precision is the reported precision of the server's clock. 179 Precision time.Duration 180 181 // Stratum is the "stratum level" of the server. The smaller the number, 182 // the closer the server is to the reference clock. Stratum 1 servers are 183 // attached directly to the reference clock. A stratum value of 0 184 // indicates the "kiss of death," which typically occurs when the client 185 // issues too many requests to the server in a short period of time. 186 Stratum uint8 187 188 // ReferenceID is a 32-bit identifier identifying the server or 189 // reference clock. 190 ReferenceID uint32 191 192 // ReferenceTime is the time when the server's system clock was last 193 // set or corrected. 194 ReferenceTime time.Time 195 196 // RootDelay is the server's estimated aggregate round-trip-time delay to 197 // the stratum 1 server. 198 RootDelay time.Duration 199 200 // RootDispersion is the server's estimated maximum measurement error 201 // relative to the stratum 1 server. 202 RootDispersion time.Duration 203 204 // RootDistance is an estimate of the total synchronization distance 205 // between the client and the stratum 1 server. 206 RootDistance time.Duration 207 208 // Leap indicates whether a leap second should be added or removed from 209 // the current month's last minute. 210 Leap LeapIndicator 211 212 // MinError is a lower bound on the error between the client and server 213 // clocks. When the client and server are not synchronized to the same 214 // clock, the reported timestamps may appear to violate the principle of 215 // causality. In other words, the NTP server's response may indicate 216 // that a message was received before it was sent. In such cases, the 217 // minimum error may be useful. 218 MinError time.Duration 219 220 // KissCode is a 4-character string describing the reason for a 221 // "kiss of death" response (stratum = 0). For a list of standard kiss 222 // codes, see https://tools.ietf.org/html/rfc5905#section-7.4. 223 KissCode string 224 225 // Poll is the maximum interval between successive NTP polling messages. 226 // It is not relevant for simple NTP clients like this one. 227 Poll time.Duration 228 } 229 230 // Validate checks if the response is valid for the purposes of time 231 // synchronization. 232 func (r *Response) Validate() error { 233 // Handle invalid stratum values. 234 if r.Stratum == 0 { 235 return fmt.Errorf("kiss of death received: %s", r.KissCode) 236 } 237 if r.Stratum >= maxStratum { 238 return errors.New("invalid stratum in response") 239 } 240 241 // Handle invalid leap second indicator. 242 if r.Leap == LeapNotInSync { 243 return errors.New("invalid leap second") 244 } 245 246 // Estimate the "freshness" of the time. If it exceeds the maximum 247 // polling interval (~36 hours), then it cannot be considered "fresh". 248 freshness := r.Time.Sub(r.ReferenceTime) 249 if freshness > maxPollInterval { 250 return errors.New("server clock not fresh") 251 } 252 253 // Calculate the peer synchronization distance, lambda: 254 // lambda := RootDelay/2 + RootDispersion 255 // If this value exceeds MAXDISP (16s), then the time is not suitable 256 // for synchronization purposes. 257 // https://tools.ietf.org/html/rfc5905#appendix-A.5.1.1. 258 lambda := r.RootDelay/2 + r.RootDispersion 259 if lambda > maxDispersion { 260 return errors.New("invalid dispersion") 261 } 262 263 // If the server's transmit time is before its reference time, the 264 // response is invalid. 265 if r.Time.Before(r.ReferenceTime) { 266 return errors.New("invalid time reported") 267 } 268 269 // nil means the response is valid. 270 return nil 271 } 272 273 // parseTime parses the NTP packet along with the packet receive time to 274 // generate a Response record. 275 func parseTime(m *msg, recvTime ntpTime) *Response { 276 r := &Response{ 277 Time: m.TransmitTime.Time(), 278 ClockOffset: offset(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime), 279 RTT: rtt(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime), 280 Precision: toInterval(m.Precision), 281 Stratum: m.Stratum, 282 ReferenceID: m.ReferenceID, 283 ReferenceTime: m.ReferenceTime.Time(), 284 RootDelay: m.RootDelay.Duration(), 285 RootDispersion: m.RootDispersion.Duration(), 286 Leap: m.getLeap(), 287 MinError: minError(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime), 288 Poll: toInterval(m.Poll), 289 } 290 291 // Calculate values depending on other calculated values 292 r.RootDistance = rootDistance(r.RTT, r.RootDelay, r.RootDispersion) 293 294 // If a kiss of death was received, interpret the reference ID as 295 // a kiss code. 296 if r.Stratum == 0 { 297 r.KissCode = kissCode(r.ReferenceID) 298 } 299 300 return r 301 } 302 303 // The following helper functions calculate additional metadata about the 304 // timestamps received from an NTP server. The timestamps returned by 305 // the server are given the following variable names: 306 // 307 // org = Origin Timestamp (client send time) 308 // rec = Receive Timestamp (server receive time) 309 // xmt = Transmit Timestamp (server reply time) 310 // dst = Destination Timestamp (client receive time) 311 312 func rtt(org, rec, xmt, dst ntpTime) time.Duration { 313 // round trip delay time 314 // rtt = (dst-org) - (xmt-rec) 315 a := dst.Time().Sub(org.Time()) 316 b := xmt.Time().Sub(rec.Time()) 317 rtt := a - b 318 if rtt < 0 { 319 rtt = 0 320 } 321 return rtt 322 } 323 324 func offset(org, rec, xmt, dst ntpTime) time.Duration { 325 // local clock offset 326 // offset = ((rec-org) + (xmt-dst)) / 2 327 a := rec.Time().Sub(org.Time()) 328 b := xmt.Time().Sub(dst.Time()) 329 return (a + b) / time.Duration(2) 330 } 331 332 func minError(org, rec, xmt, dst ntpTime) time.Duration { 333 // Each NTP response contains two pairs of send/receive timestamps. 334 // When either pair indicates a "causality violation", we calculate the 335 // error as the difference in time between them. The minimum error is 336 // the greater of the two causality violations. 337 var error0, error1 ntpTime 338 if org >= rec { 339 error0 = org - rec 340 } 341 if xmt >= dst { 342 error1 = xmt - dst 343 } 344 if error0 > error1 { 345 return error0.Duration() 346 } 347 return error1.Duration() 348 } 349 350 func rootDistance(rtt, rootDelay, rootDisp time.Duration) time.Duration { 351 // The root distance is: 352 // the maximum error due to all causes of the local clock 353 // relative to the primary server. It is defined as half the 354 // total delay plus total dispersion plus peer jitter. 355 // (https://tools.ietf.org/html/rfc5905#appendix-A.5.5.2) 356 // 357 // In the reference implementation, it is calculated as follows: 358 // rootDist = max(MINDISP, rootDelay + rtt)/2 + rootDisp 359 // + peerDisp + PHI * (uptime - peerUptime) 360 // + peerJitter 361 // For an SNTP client which sends only a single packet, most of these 362 // terms are irrelevant and become 0. 363 totalDelay := rtt + rootDelay 364 return totalDelay/2 + rootDisp 365 } 366 367 func toInterval(t int8) time.Duration { 368 switch { 369 case t > 0: 370 return time.Duration(uint64(time.Second) << uint(t)) 371 case t < 0: 372 return time.Duration(uint64(time.Second) >> uint(-t)) 373 default: 374 return time.Second 375 } 376 } 377 378 func kissCode(id uint32) string { 379 isPrintable := func(ch byte) bool { return ch >= 32 && ch <= 126 } 380 381 b := []byte{ 382 byte(id >> 24), 383 byte(id >> 16), 384 byte(id >> 8), 385 byte(id), 386 } 387 for _, ch := range b { 388 if !isPrintable(ch) { 389 return "" 390 } 391 } 392 return string(b) 393 }