mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-26 22:19:44 -08:00
Updated vendored ntp package (#681)
The github.com/beevik/ntp package was recently updated with some API changes that broke node_exporter. This commit fetches the latest version of the ntp package and brings node_exporter in line with the latest API.
This commit is contained in:
parent
859a825bb8
commit
b62c7bc0ad
|
@ -152,7 +152,7 @@ func (c *ntpCollector) Update(ch chan<- prometheus.Metric) error {
|
|||
maxerr += time.Second
|
||||
}
|
||||
|
||||
if resp.Validate() && resp.RootDistance <= *ntpMaxDistance && resp.CausalityViolation <= maxerr {
|
||||
if resp.Validate() == nil && resp.RootDistance <= *ntpMaxDistance && resp.MinError <= maxerr {
|
||||
ch <- c.sanity.mustNewConstMetric(1)
|
||||
} else {
|
||||
ch <- c.sanity.mustNewConstMetric(0)
|
||||
|
|
1
vendor/github.com/beevik/ntp/CONTRIBUTORS
generated
vendored
1
vendor/github.com/beevik/ntp/CONTRIBUTORS
generated
vendored
|
@ -4,3 +4,4 @@ Anton Tolchanov (knyar)
|
|||
Christopher Batey (chbatey)
|
||||
Meng Zhuo (mengzhuo)
|
||||
Leonid Evdokimov (darkk)
|
||||
Ask Bjørn Hansen (abh)
|
19
vendor/github.com/beevik/ntp/README.md
generated
vendored
19
vendor/github.com/beevik/ntp/README.md
generated
vendored
|
@ -4,21 +4,22 @@
|
|||
ntp
|
||||
===
|
||||
|
||||
The ntp package is an implementation of a simple NTP client. It allows you
|
||||
to connect to a remote NTP server and request the current time.
|
||||
The ntp package is an implementation of a Simple NTP (SNTP) client based on
|
||||
[RFC5905](https://tools.ietf.org/html/rfc5905). It allows you to connect to
|
||||
a remote NTP server and request the current time.
|
||||
|
||||
To request the current time, simply do the following:
|
||||
If all you care about is the current time according to a known remote NTP
|
||||
server, simply use the `Time` function:
|
||||
```go
|
||||
time, err := ntp.Time("0.beevik-ntp.pool.ntp.org")
|
||||
```
|
||||
|
||||
To request the current time along with additional metadata, use the Query
|
||||
function:
|
||||
If you want the time as well as additional metadata about the time, use the
|
||||
`Query` function instead:
|
||||
```go
|
||||
response, err := ntp.Query("0.beevik-ntp.pool.ntp.org")
|
||||
```
|
||||
|
||||
NB: if you want to use the NTP Pool in your software you should request your
|
||||
own [vendor zone](http://www.pool.ntp.org/en/vendors.html). You **must
|
||||
absolutely not use the default pool.ntp.org zone names** as the default
|
||||
configuration in your application or appliance.
|
||||
To use the NTP pool in your application, please request your own
|
||||
[vendor zone](http://www.pool.ntp.org/en/vendors.html). Avoid using
|
||||
the `[number].pool.ntp.org` zone names in your applications.
|
||||
|
|
44
vendor/github.com/beevik/ntp/RELEASE_NOTES.md
generated
vendored
Normal file
44
vendor/github.com/beevik/ntp/RELEASE_NOTES.md
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
Release v0.1.1
|
||||
==============
|
||||
|
||||
**Breaking changes**
|
||||
|
||||
* Removed the `MaxStratum` constant.
|
||||
|
||||
**Deprecations**
|
||||
|
||||
* Officially deprecated the `TimeV` function.
|
||||
|
||||
**Internal changes**
|
||||
|
||||
* Removed `minDispersion` from the `RootDistance` calculation, since the value
|
||||
was arbitrary.
|
||||
* Moved some validation into main code path so that invalid `TransmitTime` and
|
||||
`mode` responses trigger an error even when `Response.Validate` is not
|
||||
called.
|
||||
|
||||
|
||||
Release v0.1.0
|
||||
==============
|
||||
|
||||
This is the initial release of the `ntp` package. Currently it supports the following features:
|
||||
* `Time()` to query the current time according to a remote NTP server.
|
||||
* `Query()` to query multiple pieces of time-related information from a remote NTP server.
|
||||
* `QueryWithOptions()`, which is like `Query()` but with the ability to override default query options.
|
||||
|
||||
Time-related information returned by the `Query` functions includes:
|
||||
* `Time`: the time the server transmitted its response, according to the server's clock.
|
||||
* `ClockOffset`: the estimated offset of the client's clock relative to the server's clock. You may apply this offset to any local system clock reading once the query is complete.
|
||||
* `RTT`: an estimate of the round-trip-time delay between the client and the server.
|
||||
* `Precision`: the precision of the server's clock reading.
|
||||
* `Stratum`: the "stratum" level of the server, where 1 indicates a server directly connected to a reference clock, and values greater than 1 indicating the number of hops from the reference clock.
|
||||
* `ReferenceID`: A unique identifier for the NTP server that was contacted.
|
||||
* `ReferenceTime`: The time at which the server last updated its local clock setting.
|
||||
* `RootDelay`: The server's round-trip delay to the reference clock.
|
||||
* `RootDispersion`: The server's total dispersion to the referenced clock.
|
||||
* `RootDistance`: An estimate of the root synchronization distance.
|
||||
* `Leap`: The leap second indicator.
|
||||
* `MinError`: A lower bound on the clock error between the client and the server.
|
||||
* `Poll`: the maximum polling interval between successive messages on the server.
|
||||
|
||||
The `Response` structure returned by the `Query` functions also contains a `Response.Validate()` function that returns an error if any of the fields returned by the server are invalid.
|
591
vendor/github.com/beevik/ntp/ntp.go
generated
vendored
591
vendor/github.com/beevik/ntp/ntp.go
generated
vendored
|
@ -1,15 +1,17 @@
|
|||
// Copyright 2015 Brett Vickers.
|
||||
// Copyright 2015-2017 Brett Vickers.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ntp provides a simple mechanism for querying the current time from
|
||||
// a remote NTP server. See RFC 5905. Approach inspired by go-nuts post by
|
||||
// Michael Hofmann:
|
||||
// Package ntp provides an implementation of a Simple NTP (SNTP) client
|
||||
// capable of querying the current time from a remote NTP server. See
|
||||
// RFC5905 (https://tools.ietf.org/html/rfc5905) for more details.
|
||||
//
|
||||
// This approach grew out of a go-nuts post by Michael Hofmann:
|
||||
// https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ
|
||||
package ntp
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
|
@ -18,8 +20,42 @@ import (
|
|||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
// The LeapIndicator is used to warn if a leap second should be inserted
|
||||
// or deleted in the last minute of the current month.
|
||||
type LeapIndicator uint8
|
||||
|
||||
const (
|
||||
// LeapNoWarning indicates no impending leap second.
|
||||
LeapNoWarning LeapIndicator = 0
|
||||
|
||||
// LeapAddSecond indicates the last minute of the day has 61 seconds.
|
||||
LeapAddSecond = 1
|
||||
|
||||
// LeapDelSecond indicates the last minute of the day has 59 seconds.
|
||||
LeapDelSecond = 2
|
||||
|
||||
// LeapNotInSync indicates an unsynchronized leap second.
|
||||
LeapNotInSync = 3
|
||||
)
|
||||
|
||||
// Internal constants
|
||||
const (
|
||||
defaultNtpVersion = 4
|
||||
nanoPerSec = 1000000000
|
||||
maxStratum = 16
|
||||
defaultTimeout = 5 * time.Second
|
||||
maxPollInterval = (1 << 17) * time.Second
|
||||
maxDispersion = 16 * time.Second
|
||||
)
|
||||
|
||||
// Internal variables
|
||||
var (
|
||||
ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
type mode uint8
|
||||
|
||||
// NTP modes. This package uses only client mode.
|
||||
const (
|
||||
reserved mode = 0 + iota
|
||||
symmetricActive
|
||||
|
@ -31,44 +67,8 @@ const (
|
|||
reservedPrivate
|
||||
)
|
||||
|
||||
// The LeapIndicator is used to warn if a leap second should be inserted
|
||||
// or deleted in the last minute of the current month.
|
||||
type LeapIndicator uint8
|
||||
|
||||
const (
|
||||
// LeapNoWarning indicates no impending leap second
|
||||
LeapNoWarning LeapIndicator = 0
|
||||
|
||||
// LeapAddSecond indicates the last minute of the day has 61 seconds
|
||||
LeapAddSecond = 1
|
||||
|
||||
// LeapDelSecond indicates the last minute of the day has 59 seconds
|
||||
LeapDelSecond = 2
|
||||
|
||||
// LeapNotInSync indicates an unsynchronized leap second.
|
||||
LeapNotInSync = 3
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxStratum is the largest allowable NTP stratum value
|
||||
MaxStratum = 16
|
||||
|
||||
nanoPerSec = 1000000000
|
||||
|
||||
defaultNtpVersion = 4
|
||||
|
||||
maxPoll = 17 // log2 max poll interval (~36 h)
|
||||
maxDispersion = 16 // aka MAXDISP
|
||||
)
|
||||
|
||||
var (
|
||||
defaultTimeout = 5 * time.Second
|
||||
|
||||
ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
// An ntpTime is a 64-bit fixed-point (Q32.32) representation of the number of
|
||||
// seconds elapsed since the NTP epoch.
|
||||
// seconds elapsed.
|
||||
type ntpTime uint64
|
||||
|
||||
// Duration interprets the fixed-point ntpTime as a number of elapsed seconds
|
||||
|
@ -79,7 +79,7 @@ func (t ntpTime) Duration() time.Duration {
|
|||
return time.Duration(sec + frac)
|
||||
}
|
||||
|
||||
// Time interprets the fixed-point ntpTime as a an absolute time and returns
|
||||
// Time interprets the fixed-point ntpTime as an absolute time and returns
|
||||
// the corresponding time.Time value.
|
||||
func (t ntpTime) Time() time.Time {
|
||||
return ntpEpoch.Add(t.Duration())
|
||||
|
@ -90,12 +90,15 @@ func (t ntpTime) Time() time.Time {
|
|||
func toNtpTime(t time.Time) ntpTime {
|
||||
nsec := uint64(t.Sub(ntpEpoch))
|
||||
sec := nsec / nanoPerSec
|
||||
frac := (nsec - sec*nanoPerSec) << 32 / nanoPerSec
|
||||
// Round up the fractional component so that repeated conversions
|
||||
// between time.Time and ntpTime do not yield continually decreasing
|
||||
// results.
|
||||
frac := (((nsec - sec*nanoPerSec) << 32) + nanoPerSec - 1) / nanoPerSec
|
||||
return ntpTime(sec<<32 | frac)
|
||||
}
|
||||
|
||||
// An ntpTimeShort is a 32-bit fixed-point (Q16.16) representation of the
|
||||
// number of seconds elapsed since the NTP epoch.
|
||||
// number of seconds elapsed.
|
||||
type ntpTimeShort uint32
|
||||
|
||||
// Duration interprets the fixed-point ntpTimeShort as a number of elapsed
|
||||
|
@ -132,134 +135,151 @@ func (m *msg) setMode(md mode) {
|
|||
m.LiVnMode = (m.LiVnMode & 0xf8) | uint8(md)
|
||||
}
|
||||
|
||||
// setLeapIndicator modifies the leap indicator on the message.
|
||||
func (m *msg) setLeapIndicator(li LeapIndicator) {
|
||||
// setLeap modifies the leap indicator on the message.
|
||||
func (m *msg) setLeap(li LeapIndicator) {
|
||||
m.LiVnMode = (m.LiVnMode & 0x3f) | uint8(li)<<6
|
||||
}
|
||||
|
||||
// getLeapIndicator returns the leap indicator on the message.
|
||||
func (m *msg) getLeapIndicator() LeapIndicator {
|
||||
// getVersion returns the version value in the message.
|
||||
func (m *msg) getVersion() int {
|
||||
return int((m.LiVnMode >> 3) & 0x07)
|
||||
}
|
||||
|
||||
// getMode returns the mode value in the message.
|
||||
func (m *msg) getMode() mode {
|
||||
return mode(m.LiVnMode & 0x07)
|
||||
}
|
||||
|
||||
// getLeap returns the leap indicator on the message.
|
||||
func (m *msg) getLeap() LeapIndicator {
|
||||
return LeapIndicator((m.LiVnMode >> 6) & 0x03)
|
||||
}
|
||||
|
||||
// QueryOptions contains the list of configurable options that may be used with
|
||||
// the QueryWithOptions function.
|
||||
// QueryOptions contains the list of configurable options that may be used
|
||||
// with the QueryWithOptions function.
|
||||
type QueryOptions struct {
|
||||
Timeout time.Duration // defaults to 5 seconds
|
||||
Version int // NTP protocol version, defaults to 4
|
||||
Port int // NTP Server port for UDPAddr.Port, defaults to 123
|
||||
TTL int // IP TTL to use for outgoing UDP packets, defaults to system default
|
||||
Timeout time.Duration // defaults to 5 seconds
|
||||
Version int // NTP protocol version, defaults to 4
|
||||
LocalAddress string // IP address to use for the client address
|
||||
Port int // Server port, defaults to 123
|
||||
TTL int // IP TTL to use, defaults to system default
|
||||
}
|
||||
|
||||
// A Response contains time data, some of which is returned by the NTP server
|
||||
// and some of which is calculated by the client.
|
||||
type Response struct {
|
||||
Time time.Time // receive time reported by the server
|
||||
RTT time.Duration // round-trip time between client and server
|
||||
ClockOffset time.Duration // local clock offset relative to server
|
||||
Poll time.Duration // maximum polling interval
|
||||
Precision time.Duration // precision of server's system clock
|
||||
Stratum uint8 // stratum level of NTP server's clock
|
||||
ReferenceID uint32 // server's reference ID
|
||||
ReferenceTime time.Time // server's time of last clock update
|
||||
RootDelay time.Duration // server's RTT to the reference clock
|
||||
RootDispersion time.Duration // server's dispersion to the reference clock
|
||||
Leap LeapIndicator // server's leap second indicator; see RFC 5905
|
||||
// Time is the transmit time reported by the server just before it
|
||||
// responded to the client's NTP query.
|
||||
Time time.Time
|
||||
|
||||
// RootDistance is the single-packet estimate of the root synchronization
|
||||
// distance. Some SNTP clients limit-check this value before using the
|
||||
// response. For example, systemd-timesyncd uses 5.0s as an upper bound. See
|
||||
// https://tools.ietf.org/html/rfc5905#appendix-A.5.5.2
|
||||
// ClockOffset is the estimated offset of the client clock relative to
|
||||
// the server. Add this to the client's system clock time to obtain a
|
||||
// more accurate time.
|
||||
ClockOffset time.Duration
|
||||
|
||||
// RTT is the measured round-trip-time delay estimate between the client
|
||||
// and the server.
|
||||
RTT time.Duration
|
||||
|
||||
// Precision is the reported precision of the server's clock.
|
||||
Precision time.Duration
|
||||
|
||||
// Stratum is the "stratum level" of the server. The smaller the number,
|
||||
// the closer the server is to the reference clock. Stratum 1 servers are
|
||||
// attached directly to the reference clock. A stratum value of 0
|
||||
// indicates the "kiss of death," which typically occurs when the client
|
||||
// issues too many requests to the server in a short period of time.
|
||||
Stratum uint8
|
||||
|
||||
// ReferenceID is a 32-bit identifier identifying the server or
|
||||
// reference clock.
|
||||
ReferenceID uint32
|
||||
|
||||
// ReferenceTime is the time when the server's system clock was last
|
||||
// set or corrected.
|
||||
ReferenceTime time.Time
|
||||
|
||||
// RootDelay is the server's estimated aggregate round-trip-time delay to
|
||||
// the stratum 1 server.
|
||||
RootDelay time.Duration
|
||||
|
||||
// RootDispersion is the server's estimated maximum measurement error
|
||||
// relative to the stratum 1 server.
|
||||
RootDispersion time.Duration
|
||||
|
||||
// RootDistance is an estimate of the total synchronization distance
|
||||
// between the client and the stratum 1 server.
|
||||
RootDistance time.Duration
|
||||
|
||||
// CausalityViolation is a time duration representing the amount of
|
||||
// causality violation between two sets of timestamps. It may be used as a
|
||||
// lower bound on current time synchronization error betwen local and NTP
|
||||
// clock. A leap second may contribute as much as 1 second of causality violation.
|
||||
CausalityViolation time.Duration
|
||||
// Leap indicates whether a leap second should be added or removed from
|
||||
// the current month's last minute.
|
||||
Leap LeapIndicator
|
||||
|
||||
// MinError is a lower bound on the error between the client and server
|
||||
// clocks. When the client and server are not synchronized to the same
|
||||
// clock, the reported timestamps may appear to violate the principle of
|
||||
// causality. In other words, the NTP server's response may indicate
|
||||
// that a message was received before it was sent. In such cases, the
|
||||
// minimum error may be useful.
|
||||
MinError time.Duration
|
||||
|
||||
// Poll is the maximum interval between successive NTP polling messages.
|
||||
// It is not relevant for simple NTP clients like this one.
|
||||
Poll time.Duration
|
||||
}
|
||||
|
||||
// Validate checks if the response is valid for the purposes of time
|
||||
// synchronization.
|
||||
func (r *Response) Validate() bool {
|
||||
// Reference Timestamp: Time when the system clock was last set or
|
||||
// corrected. Semantics of this value seems to vary across NTP server
|
||||
// implementations: it may be both NTP-clock time and system wall-clock
|
||||
// time of this event. :-( So (T3 - ReferenceTime) is not true
|
||||
// "freshness" as it may be actually NEGATIVE sometimes.
|
||||
freshness := r.Time.Sub(r.ReferenceTime)
|
||||
func (r *Response) Validate() error {
|
||||
// Handle invalid stratum values.
|
||||
if r.Stratum == 0 {
|
||||
return errors.New("kiss of death received")
|
||||
}
|
||||
if r.Stratum >= maxStratum {
|
||||
return errors.New("invalid stratum in response")
|
||||
}
|
||||
|
||||
// (Lambda := RootDelay/2 + RootDispersion) check against MAXDISP (16s)
|
||||
// is required as ntp.org ntpd may report sane other fields while
|
||||
// giving quite erratic clock. The check is declared in packet() at
|
||||
// Handle invalid leap second indicator.
|
||||
if r.Leap == LeapNotInSync {
|
||||
return errors.New("invalid leap second")
|
||||
}
|
||||
|
||||
// Estimate the "freshness" of the time. If it exceeds the maximum
|
||||
// polling interval (~36 hours), then it cannot be considered "fresh".
|
||||
freshness := r.Time.Sub(r.ReferenceTime)
|
||||
if freshness > maxPollInterval {
|
||||
return errors.New("server clock not fresh")
|
||||
}
|
||||
|
||||
// Calculate the peer synchronization distance, lambda:
|
||||
// lambda := RootDelay/2 + RootDispersion
|
||||
// If this value exceeds MAXDISP (16s), then the time is not suitable
|
||||
// for synchronization purposes.
|
||||
// https://tools.ietf.org/html/rfc5905#appendix-A.5.1.1.
|
||||
lambda := r.RootDelay/2 + r.RootDispersion
|
||||
if lambda > maxDispersion {
|
||||
return errors.New("invalid dispersion")
|
||||
}
|
||||
|
||||
// `r.RTT > 0` check is not included as it does not depend on the
|
||||
// packet itself, but also depends on clock _speed_. It's indicator
|
||||
// that local clock run faster than remote one, so (T4-T1) < (T3-T2),
|
||||
// but it may be local clock issue.
|
||||
// E.g. T1/T2/T3/T4 = 0/10/20/1 leads to RTT = -9s.
|
||||
// If the server's transmit time is before its reference time, the
|
||||
// response is invalid.
|
||||
if r.Time.Before(r.ReferenceTime) {
|
||||
return errors.New("invalid time reported")
|
||||
}
|
||||
|
||||
return r.Leap != LeapNotInSync && // RFC5905, packet()
|
||||
0 < r.Stratum && r.Stratum < MaxStratum && // RFC5905, packet()
|
||||
lambda < maxDispersion*time.Second && // RFC5905, packet()
|
||||
!r.Time.Before(r.ReferenceTime) && // RFC5905, packet(), reftime <= xmt ~~ !(xmt < reftime)
|
||||
freshness <= (1<<maxPoll)*time.Second && // ntpdate uses 24h as a heuristics instead of ~36h derived from MAXPOLL
|
||||
ntpEpoch.Before(r.Time) && // sanity
|
||||
ntpEpoch.Before(r.ReferenceTime) // sanity
|
||||
// nil means the response is valid.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Response) rootDistance() time.Duration {
|
||||
// RFC5905 suggests more strict check against _peer_ in fit(), that
|
||||
// root_dist should be less than MAXDIST + PHI * LOG2D(s.poll).
|
||||
// MAXPOLL is 17, so it is approximately at most (1s + 15e-6 * 2**17) =
|
||||
// 2.96608 s, but MAXDIST and MAXPOLL are confugurable values in the
|
||||
// reference implementation, so only MAXDISP check has hardcoded value
|
||||
// in Validate().
|
||||
//
|
||||
// root_dist should also have following summands
|
||||
// + Dispersion towards the peer
|
||||
// + jitter of the link to the peer
|
||||
// + PHI * (current_uptime - peer->uptime_of_last_update)
|
||||
// but all these values are 0 if only single NTP packet was sent.
|
||||
rtt := r.RTT
|
||||
if rtt < 0 {
|
||||
rtt = 0
|
||||
}
|
||||
return (rtt+r.RootDelay)/2 + r.RootDispersion
|
||||
}
|
||||
|
||||
func (r *Response) causalityViolation() time.Duration {
|
||||
// SNTP query has four timestamps for consecutive events: T1, T2, T3
|
||||
// and T4. T1 and T4 use local clock, T2 and T3 use NTP clock.
|
||||
// RTT = (T4 - T1) - (T3 - T2) = T4 - T3 + T2 - T1
|
||||
// Offset = (T2 + T3)/2 - (T4 + T1)/2 = (-T4 + T3 + T2 - T1) / 2
|
||||
// => T2 - T1 = RTT/2 + Offset && T4 - T3 = RTT/2 - Offset
|
||||
// If system wall-clock is synced to NTP-clock then T2 >= T1 && T4 >= T3.
|
||||
// This check may be useful against chrony NTP daemon as it starts
|
||||
// relaying sane NTP clock before system wall-clock is actually adjusted.
|
||||
violation := r.RTT / 2
|
||||
if r.ClockOffset > 0 {
|
||||
violation -= r.ClockOffset
|
||||
} else {
|
||||
violation += r.ClockOffset
|
||||
}
|
||||
if violation < 0 {
|
||||
return -violation
|
||||
}
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
// Query returns the current time from the remote server host. It also returns
|
||||
// additional information about the exchanged time information.
|
||||
// Query returns a response from the remote NTP server host. It contains
|
||||
// the time at which the server transmitted the response as well as other
|
||||
// useful information about the time and the remote server.
|
||||
func Query(host string) (*Response, error) {
|
||||
return QueryWithOptions(host, QueryOptions{})
|
||||
}
|
||||
|
||||
// QueryWithOptions returns the current time from the remote server host.
|
||||
// It also returns additional information about the exchanged time
|
||||
// information. It allows the specification of additional query options.
|
||||
// QueryWithOptions performs the same function as Query but allows for the
|
||||
// customization of several query options.
|
||||
func QueryWithOptions(host string, opt QueryOptions) (*Response, error) {
|
||||
m, now, err := getTime(host, opt)
|
||||
if err != nil {
|
||||
|
@ -268,64 +288,71 @@ func QueryWithOptions(host string, opt QueryOptions) (*Response, error) {
|
|||
return parseTime(m, now), nil
|
||||
}
|
||||
|
||||
// parseTime parses SNTP packet paired with the packet arrival time (dst) and
|
||||
// returns Response having SNTP packet data converted to go types.
|
||||
func parseTime(m *msg, dst ntpTime) *Response {
|
||||
r := &Response{
|
||||
Time: m.TransmitTime.Time(),
|
||||
RTT: rtt(m.OriginTime, m.ReceiveTime, m.TransmitTime, dst),
|
||||
ClockOffset: offset(m.OriginTime, m.ReceiveTime, m.TransmitTime, dst),
|
||||
Poll: toInterval(m.Poll),
|
||||
Precision: toInterval(m.Precision),
|
||||
Stratum: m.Stratum,
|
||||
ReferenceID: m.ReferenceID,
|
||||
ReferenceTime: m.ReferenceTime.Time(),
|
||||
RootDelay: m.RootDelay.Duration(),
|
||||
RootDispersion: m.RootDispersion.Duration(),
|
||||
Leap: m.getLeapIndicator(),
|
||||
// TimeV returns the current time using information from a remote NTP server.
|
||||
// On error, it returns the local system time. The version may be 2, 3, or 4.
|
||||
//
|
||||
// Deprecated: TimeV is deprecated. Use QueryWithOptions instead.
|
||||
func TimeV(host string, version int) (time.Time, error) {
|
||||
m, recvTime, err := getTime(host, QueryOptions{Version: version})
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
}
|
||||
|
||||
// these are exported as values to preserve API style consistency
|
||||
r.RootDistance = r.rootDistance()
|
||||
r.CausalityViolation = r.causalityViolation()
|
||||
|
||||
// https://tools.ietf.org/html/rfc5905#section-7.3
|
||||
if r.Stratum == 0 {
|
||||
r.Stratum = MaxStratum
|
||||
r := parseTime(m, recvTime)
|
||||
err = r.Validate()
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
}
|
||||
|
||||
return r
|
||||
// Use the clock offset to calculate the time.
|
||||
return time.Now().Add(r.ClockOffset), nil
|
||||
}
|
||||
|
||||
// getTime returns SNTP packet & DestinationTime timestamp.
|
||||
// Time returns the current time using information from a remote NTP server.
|
||||
// It uses version 4 of the NTP protocol. On error, it returns the local
|
||||
// system time.
|
||||
func Time(host string) (time.Time, error) {
|
||||
return TimeV(host, defaultNtpVersion)
|
||||
}
|
||||
|
||||
// getTime performs the NTP server query and returns the response message
|
||||
// along with the local system time it was received.
|
||||
func getTime(host string, opt QueryOptions) (*msg, ntpTime, error) {
|
||||
if opt.Version == 0 {
|
||||
opt.Version = defaultNtpVersion
|
||||
}
|
||||
|
||||
if opt.Version < 2 || opt.Version > 4 {
|
||||
panic("ntp: invalid version number")
|
||||
return nil, 0, errors.New("invalid protocol version requested")
|
||||
}
|
||||
|
||||
if opt.Timeout == 0 {
|
||||
opt.Timeout = defaultTimeout
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp", host+":123")
|
||||
// Resolve the remote NTP server address.
|
||||
raddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(host, "123"))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Resolve the local address if specified as an option.
|
||||
var laddr *net.UDPAddr
|
||||
if opt.LocalAddress != "" {
|
||||
laddr, err = net.ResolveUDPAddr("udp", net.JoinHostPort(opt.LocalAddress, "0"))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Override the port if requested.
|
||||
if opt.Port != 0 {
|
||||
raddr.Port = opt.Port
|
||||
}
|
||||
|
||||
con, err := net.DialUDP("udp", nil, raddr)
|
||||
// Prepare a "connection" to the remote server.
|
||||
con, err := net.DialUDP("udp", laddr, raddr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer con.Close()
|
||||
|
||||
// Set a TTL for the packet if requested.
|
||||
if opt.TTL != 0 {
|
||||
ipcon := ipv4.NewConn(con)
|
||||
err = ipcon.SetTTL(opt.TTL)
|
||||
|
@ -334,110 +361,168 @@ func getTime(host string, opt QueryOptions) (*msg, ntpTime, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Set a timeout on the connection.
|
||||
if opt.Timeout == 0 {
|
||||
opt.Timeout = defaultTimeout
|
||||
}
|
||||
con.SetDeadline(time.Now().Add(opt.Timeout))
|
||||
|
||||
m := new(msg)
|
||||
m.setMode(client)
|
||||
m.setVersion(opt.Version)
|
||||
m.setLeapIndicator(LeapNotInSync)
|
||||
// Allocate a message to hold the response.
|
||||
recvMsg := new(msg)
|
||||
|
||||
xmtTime := time.Now()
|
||||
xmt := toNtpTime(xmtTime)
|
||||
m.TransmitTime = xmt
|
||||
// Allocate a message to hold the query.
|
||||
xmitMsg := new(msg)
|
||||
xmitMsg.setMode(client)
|
||||
xmitMsg.setVersion(opt.Version)
|
||||
xmitMsg.setLeap(LeapNotInSync)
|
||||
|
||||
err = binary.Write(con, binary.BigEndian, m)
|
||||
// To ensure privacy and prevent spoofing, try to use a random 64-bit
|
||||
// value for the TransmitTime. If crypto/rand couldn't generate a
|
||||
// random value, fall back to using the system clock. Keep track of
|
||||
// when the messsage was actually sent.
|
||||
r := make([]byte, 8)
|
||||
_, err = rand.Read(r)
|
||||
var sendTime time.Time
|
||||
if err == nil {
|
||||
xmitMsg.TransmitTime = ntpTime(binary.BigEndian.Uint64(r))
|
||||
sendTime = time.Now()
|
||||
} else {
|
||||
sendTime = time.Now()
|
||||
xmitMsg.TransmitTime = toNtpTime(sendTime)
|
||||
}
|
||||
|
||||
// Transmit the query.
|
||||
err = binary.Write(con, binary.BigEndian, xmitMsg)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
err = binary.Read(con, binary.BigEndian, m)
|
||||
// Receive the response.
|
||||
err = binary.Read(con, binary.BigEndian, recvMsg)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
delta := time.Since(xmtTime) // uses monotonic clock @ Go 1.9+, NB: delta != RTT
|
||||
dst := toNtpTime(xmtTime.Add(delta))
|
||||
// Keep track of the time the response was received.
|
||||
delta := time.Since(sendTime)
|
||||
if delta < 0 {
|
||||
// The system clock may have been set backwards since the packet was
|
||||
// transmitted. In go 1.9 and later, time.Since ensures that a
|
||||
// monotonic clock is used, and delta can never be less than zero.
|
||||
// In versions before 1.9, we have to check.
|
||||
return nil, 0, errors.New("client clock ticked backwards")
|
||||
}
|
||||
recvTime := toNtpTime(sendTime.Add(delta))
|
||||
|
||||
// It's possible to use random uint64 as client's `TransmitTime` field,
|
||||
// it has better privacy (clock of the node is not disclosed in
|
||||
// plain-text), better UDP packet spoofing resistance (blind attacker
|
||||
// has to guess both port and the uint64 value), and OpenNTPD behaves
|
||||
// like that. But math/rand is not secure enough for the purpose,
|
||||
// crypto/rand takes 64 bits of entropy for every outgoing packet and
|
||||
// CSPRNG from crypto/rand/rand_unix is not available: see
|
||||
// https://github.com/golang/go/issues/13820
|
||||
// A packet is bogus if the origin timestamp t1 in the packet does not
|
||||
// match the xmt state variable T1.
|
||||
// -- https://tools.ietf.org/html/rfc5905#section-8
|
||||
if m.OriginTime != xmt {
|
||||
return nil, 0, errors.New("response OriginTime != query TransmitTime") // spoofed packet?
|
||||
// Check for invalid fields.
|
||||
if recvMsg.getMode() != server {
|
||||
return nil, 0, errors.New("invalid mode in response")
|
||||
}
|
||||
if recvMsg.TransmitTime == ntpTime(0) {
|
||||
return nil, 0, errors.New("invalid transmit time in response")
|
||||
}
|
||||
if recvMsg.OriginTime != xmitMsg.TransmitTime {
|
||||
return nil, 0, errors.New("server response mismatch")
|
||||
}
|
||||
if recvMsg.ReceiveTime > recvMsg.TransmitTime {
|
||||
return nil, 0, errors.New("server clock ticked backwards")
|
||||
}
|
||||
|
||||
if m.OriginTime > dst { // Go 1.9 has monotonic clock preventing that, but 1.8 has not, so it's not panic()
|
||||
return nil, 0, errors.New("client clock tick backwards")
|
||||
}
|
||||
// Correct the received message's origin time using the actual send
|
||||
// time.
|
||||
recvMsg.OriginTime = toNtpTime(sendTime)
|
||||
|
||||
if m.ReceiveTime > m.TransmitTime {
|
||||
return nil, 0, errors.New("server clock tick backwards")
|
||||
}
|
||||
|
||||
return m, dst, nil
|
||||
return recvMsg, recvTime, nil
|
||||
}
|
||||
|
||||
// TimeV returns the current time from the remote server host using the
|
||||
// requested version of the NTP protocol. On error, it returns the local time.
|
||||
// The version may be 2, 3, or 4.
|
||||
func TimeV(host string, version int) (time.Time, error) {
|
||||
m, dst, err := getTime(host, QueryOptions{Version: version})
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
// parseTime parses the NTP packet along with the packet receive time to
|
||||
// generate a Response record.
|
||||
func parseTime(m *msg, recvTime ntpTime) *Response {
|
||||
r := &Response{
|
||||
Time: m.TransmitTime.Time(),
|
||||
ClockOffset: offset(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime),
|
||||
RTT: rtt(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime),
|
||||
Precision: toInterval(m.Precision),
|
||||
Stratum: m.Stratum,
|
||||
ReferenceID: m.ReferenceID,
|
||||
ReferenceTime: m.ReferenceTime.Time(),
|
||||
RootDelay: m.RootDelay.Duration(),
|
||||
RootDispersion: m.RootDispersion.Duration(),
|
||||
Leap: m.getLeap(),
|
||||
MinError: minError(m.OriginTime, m.ReceiveTime, m.TransmitTime, recvTime),
|
||||
Poll: toInterval(m.Poll),
|
||||
}
|
||||
r := parseTime(m, dst)
|
||||
if !r.Validate() {
|
||||
return time.Now(), errors.New("invalid SNTP reply")
|
||||
|
||||
// Calculate values depending on other calculated values
|
||||
r.RootDistance = rootDistance(r.RTT, r.RootDelay, r.RootDispersion)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// The following helper functions calculate additional metadata about the
|
||||
// timestamps received from an NTP server. The timestamps returned by
|
||||
// the server are given the following variable names:
|
||||
//
|
||||
// org = Origin Timestamp (client send time)
|
||||
// rec = Receive Timestamp (server receive time)
|
||||
// xmt = Transmit Timestamp (server reply time)
|
||||
// dst = Destination Timestamp (client receive time)
|
||||
|
||||
func rtt(org, rec, xmt, dst ntpTime) time.Duration {
|
||||
// round trip delay time
|
||||
// rtt = (dst-org) - (xmt-rec)
|
||||
a := dst.Time().Sub(org.Time())
|
||||
b := xmt.Time().Sub(rec.Time())
|
||||
rtt := a - b
|
||||
if rtt < 0 {
|
||||
rtt = 0
|
||||
}
|
||||
// An SNTP client implementing the on-wire protocol has a single server
|
||||
// and no dependent clients. It can operate with any subset of the NTP
|
||||
// on-wire protocol, the simplest approach using only the transmit
|
||||
// timestamp of the server packet and ignoring all other fields.
|
||||
// -- https://tools.ietf.org/html/rfc5905#section-14
|
||||
return time.Now().Add(r.ClockOffset), nil
|
||||
return rtt
|
||||
}
|
||||
|
||||
// Time returns the current time from the remote server host using version 4 of
|
||||
// the NTP protocol. On error, it returns the local time.
|
||||
func Time(host string) (time.Time, error) {
|
||||
return TimeV(host, defaultNtpVersion)
|
||||
}
|
||||
|
||||
func rtt(t1, t2, t3, t4 ntpTime) time.Duration {
|
||||
// round trip delay time (https://tools.ietf.org/html/rfc5905#section-8)
|
||||
// T1 = client send time
|
||||
// T2 = server receive time
|
||||
// T3 = server reply time
|
||||
// T4 = client receive time
|
||||
//
|
||||
// RTT d:
|
||||
// d = (T4-T1) - (T3-T2)
|
||||
a := t4.Time().Sub(t1.Time())
|
||||
b := t3.Time().Sub(t2.Time())
|
||||
return a - b
|
||||
}
|
||||
|
||||
func offset(t1, t2, t3, t4 ntpTime) time.Duration {
|
||||
// local offset equation (https://tools.ietf.org/html/rfc5905#section-8)
|
||||
// T1 = client send time
|
||||
// T2 = server receive time
|
||||
// T3 = server reply time
|
||||
// T4 = client receive time
|
||||
//
|
||||
// Local clock offset t:
|
||||
// t = ((T2-T1) + (T3-T4)) / 2
|
||||
a := t2.Time().Sub(t1.Time())
|
||||
b := t3.Time().Sub(t4.Time())
|
||||
func offset(org, rec, xmt, dst ntpTime) time.Duration {
|
||||
// local clock offset
|
||||
// offset = ((rec-org) + (xmt-dst)) / 2
|
||||
a := rec.Time().Sub(org.Time())
|
||||
b := xmt.Time().Sub(dst.Time())
|
||||
return (a + b) / time.Duration(2)
|
||||
}
|
||||
|
||||
func minError(org, rec, xmt, dst ntpTime) time.Duration {
|
||||
// Each NTP response contains two pairs of send/receive timestamps.
|
||||
// When either pair indicates a "causality violation", we calculate the
|
||||
// error as the difference in time between them. The minimum error is
|
||||
// the greater of the two causality violations.
|
||||
var error0, error1 ntpTime
|
||||
if org >= rec {
|
||||
error0 = org - rec
|
||||
}
|
||||
if xmt >= dst {
|
||||
error1 = xmt - dst
|
||||
}
|
||||
if error0 > error1 {
|
||||
return error0.Duration()
|
||||
}
|
||||
return error1.Duration()
|
||||
}
|
||||
|
||||
func rootDistance(rtt, rootDelay, rootDisp time.Duration) time.Duration {
|
||||
// The root distance is:
|
||||
// the maximum error due to all causes of the local clock
|
||||
// relative to the primary server. It is defined as half the
|
||||
// total delay plus total dispersion plus peer jitter.
|
||||
// (https://tools.ietf.org/html/rfc5905#appendix-A.5.5.2)
|
||||
//
|
||||
// In the reference implementation, it is calculated as follows:
|
||||
// rootDist = max(MINDISP, rootDelay + rtt)/2 + rootDisp
|
||||
// + peerDisp + PHI * (uptime - peerUptime)
|
||||
// + peerJitter
|
||||
// For an SNTP client which sends only a single packet, most of these
|
||||
// terms are irrelevant and become 0.
|
||||
totalDelay := rtt + rootDelay
|
||||
return totalDelay/2 + rootDisp
|
||||
}
|
||||
|
||||
func toInterval(t int8) time.Duration {
|
||||
switch {
|
||||
case t > 0:
|
||||
|
|
20
vendor/vendor.json
vendored
20
vendor/vendor.json
vendored
|
@ -2,12 +2,6 @@
|
|||
"comment": "",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "0Tugz8gj9KqqVj6JLkXUA7BXas4=",
|
||||
"path": "github.com/sirupsen/logrus",
|
||||
"revision": "0208149b40d863d2c1a2f8fe5753096a9cf2cc8b",
|
||||
"revisionTime": "2017-02-27T12:44:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "KmjnydoAbofMieIWm+it5OWERaM=",
|
||||
"path": "github.com/alecthomas/template",
|
||||
|
@ -27,10 +21,12 @@
|
|||
"revisionTime": "2015-10-22T06:55:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "X73SC/5YR0afYD/tq802Shxb0UI=",
|
||||
"checksumSHA1": "OXcULIfKv/Xcsa4o1By1lY0s+AI=",
|
||||
"path": "github.com/beevik/ntp",
|
||||
"revision": "74e5133786235ede462c83c17fa3b34653901719",
|
||||
"revisionTime": "2017-08-25T09:16:23Z"
|
||||
"revision": "802074b1b037c59dbdbee7e196ad91b51d0ec844",
|
||||
"revisionTime": "2017-10-03T23:10:51Z",
|
||||
"version": "v0.1.1",
|
||||
"versionExact": "v0.1.1"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "spyv5/YFBjYyZLZa1U2LBfDR8PM=",
|
||||
|
@ -182,6 +178,12 @@
|
|||
"revision": "a66a2f8b6f3fe82a95a1bed0bb3705bac8031717",
|
||||
"revisionTime": "2017-06-07T19:36:46Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "0Tugz8gj9KqqVj6JLkXUA7BXas4=",
|
||||
"path": "github.com/sirupsen/logrus",
|
||||
"revision": "0208149b40d863d2c1a2f8fe5753096a9cf2cc8b",
|
||||
"revisionTime": "2017-02-27T12:44:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "uozMgPjB4AggpuuJkGq3FgAs4CA=",
|
||||
"path": "github.com/soundcloud/go-runit/runit",
|
||||
|
|
Loading…
Reference in a new issue