Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
b1908f6a
Commit
b1908f6a
authored
Feb 15, 2016
by
Péter Szilágyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
psp/discovery: NTP sanity check clock drift in case of expirations
parent
7841f0cc
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
128 additions
and
4 deletions
+128
-4
ntp.go
p2p/discover/ntp.go
+99
-0
udp.go
p2p/discover/udp.go
+29
-4
No files found.
p2p/discover/ntp.go
0 → 100644
View file @
b1908f6a
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains the NTP time drift detection via the SNTP protocol:
// https://tools.ietf.org/html/rfc4330
package
discover
import
(
"net"
"sort"
"time"
)
// ntpPool is the NTP server to query for the current time
const
ntpPool
=
"pool.ntp.org"
// durationSlice attaches the methods of sort.Interface to []time.Duration,
// sorting in increasing order.
type
durationSlice
[]
time
.
Duration
func
(
s
durationSlice
)
Len
()
int
{
return
len
(
s
)
}
func
(
s
durationSlice
)
Less
(
i
,
j
int
)
bool
{
return
s
[
i
]
<
s
[
j
]
}
func
(
s
durationSlice
)
Swap
(
i
,
j
int
)
{
s
[
i
],
s
[
j
]
=
s
[
j
],
s
[
i
]
}
// sntpDrift does a naive time resolution against an NTP server and returns the
// measured drift. This method uses the simple version of NTP. It's not precise
// but should be fine for these purposes.
//
// Note, it executes two extra measurements compared to the number of requested
// ones to be able to discard the two extremes as outliers.
func
sntpDrift
(
measurements
int
)
(
time
.
Duration
,
error
)
{
// Resolve the address of the NTP server
addr
,
err
:=
net
.
ResolveUDPAddr
(
"udp"
,
ntpPool
+
":123"
)
if
err
!=
nil
{
return
0
,
err
}
// Construct the time request (empty package with only 2 fields set):
// Bits 3-5: Protocol version, 3
// Bits 6-8: Mode of operation, client, 3
request
:=
make
([]
byte
,
48
)
request
[
0
]
=
3
<<
3
|
3
// Execute each of the measurements
drifts
:=
[]
time
.
Duration
{}
for
i
:=
0
;
i
<
measurements
+
2
;
i
++
{
// Dial the NTP server and send the time retrieval request
conn
,
err
:=
net
.
DialUDP
(
"udp"
,
nil
,
addr
)
if
err
!=
nil
{
return
0
,
err
}
defer
conn
.
Close
()
sent
:=
time
.
Now
()
if
_
,
err
=
conn
.
Write
(
request
);
err
!=
nil
{
return
0
,
err
}
// Retrieve the reply and calculate the elapsed time
conn
.
SetDeadline
(
time
.
Now
()
.
Add
(
5
*
time
.
Second
))
reply
:=
make
([]
byte
,
48
)
if
_
,
err
=
conn
.
Read
(
reply
);
err
!=
nil
{
return
0
,
err
}
elapsed
:=
time
.
Since
(
sent
)
// Reconstruct the time from the reply data
sec
:=
uint64
(
reply
[
43
])
|
uint64
(
reply
[
42
])
<<
8
|
uint64
(
reply
[
41
])
<<
16
|
uint64
(
reply
[
40
])
<<
24
frac
:=
uint64
(
reply
[
47
])
|
uint64
(
reply
[
46
])
<<
8
|
uint64
(
reply
[
45
])
<<
16
|
uint64
(
reply
[
44
])
<<
24
nanosec
:=
sec
*
1e9
+
(
frac
*
1e9
)
>>
32
t
:=
time
.
Date
(
1900
,
1
,
1
,
0
,
0
,
0
,
0
,
time
.
UTC
)
.
Add
(
time
.
Duration
(
nanosec
))
.
Local
()
// Calculate the drift based on an assumed answer time of RRT/2
drifts
=
append
(
drifts
,
sent
.
Sub
(
t
)
+
elapsed
/
2
)
}
// Calculate average drif (drop two extremities to avoid outliers)
sort
.
Sort
(
durationSlice
(
drifts
))
drift
:=
time
.
Duration
(
0
)
for
i
:=
1
;
i
<
len
(
drifts
)
-
1
;
i
++
{
drift
+=
drifts
[
i
]
}
return
drift
/
time
.
Duration
(
measurements
),
nil
}
p2p/discover/udp.go
View file @
b1908f6a
...
@@ -51,6 +51,9 @@ const (
...
@@ -51,6 +51,9 @@ const (
respTimeout
=
500
*
time
.
Millisecond
respTimeout
=
500
*
time
.
Millisecond
sendTimeout
=
500
*
time
.
Millisecond
sendTimeout
=
500
*
time
.
Millisecond
expiration
=
20
*
time
.
Second
expiration
=
20
*
time
.
Second
ntpThreshold
=
32
// Continuous timeouts after which to check NTP
driftThreshold
=
10
*
time
.
Second
// Allowed clock drift before warning user
)
)
// RPC packet types
// RPC packet types
...
@@ -316,13 +319,14 @@ func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
...
@@ -316,13 +319,14 @@ func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
}
}
}
}
// loop runs in its own goroutin. it keeps track of
// loop runs in its own goroutin
e
. it keeps track of
// the refresh timer and the pending reply queue.
// the refresh timer and the pending reply queue.
func
(
t
*
udp
)
loop
()
{
func
(
t
*
udp
)
loop
()
{
var
(
var
(
plist
=
list
.
New
()
plist
=
list
.
New
()
timeout
=
time
.
NewTimer
(
0
)
timeout
=
time
.
NewTimer
(
0
)
nextTimeout
*
pending
// head of plist when timeout was last reset
nextTimeout
*
pending
// head of plist when timeout was last reset
contTimeouts
=
0
// number of continuous timeouts to do NTP checks
)
)
<-
timeout
.
C
// ignore first timeout
<-
timeout
.
C
// ignore first timeout
defer
timeout
.
Stop
()
defer
timeout
.
Stop
()
...
@@ -377,20 +381,41 @@ func (t *udp) loop() {
...
@@ -377,20 +381,41 @@ func (t *udp) loop() {
p
.
errc
<-
nil
p
.
errc
<-
nil
plist
.
Remove
(
el
)
plist
.
Remove
(
el
)
}
}
// Reset the continuous timeout counter (time drift detection)
contTimeouts
=
0
}
}
}
}
r
.
matched
<-
matched
r
.
matched
<-
matched
case
now
:=
<-
timeout
.
C
:
case
now
:=
<-
timeout
.
C
:
nextTimeout
=
nil
nextTimeout
=
nil
// Notify and remove callbacks whose deadline is in the past.
// Notify and remove callbacks whose deadline is in the past.
for
el
:=
plist
.
Front
();
el
!=
nil
;
el
=
el
.
Next
()
{
for
el
:=
plist
.
Front
();
el
!=
nil
;
el
=
el
.
Next
()
{
p
:=
el
.
Value
.
(
*
pending
)
p
:=
el
.
Value
.
(
*
pending
)
if
now
.
After
(
p
.
deadline
)
||
now
.
Equal
(
p
.
deadline
)
{
if
now
.
After
(
p
.
deadline
)
||
now
.
Equal
(
p
.
deadline
)
{
p
.
errc
<-
errTimeout
p
.
errc
<-
errTimeout
plist
.
Remove
(
el
)
plist
.
Remove
(
el
)
contTimeouts
++
}
}
}
}
// If we've accumulated too many timeouts, do an NTP time sync check
if
contTimeouts
>
ntpThreshold
{
go
func
()
{
drift
,
err
:=
sntpDrift
(
3
)
switch
{
case
err
!=
nil
:
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"No UDP connectivity, maybe blocked by firewall? (%v)"
,
err
)
case
drift
<
-
driftThreshold
||
drift
>
driftThreshold
:
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"System clock seems off by %v, which can prevent network connectivity"
,
drift
)
default
:
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Sanity NTP check reported %v drift, all ok"
,
drift
)
}
}()
contTimeouts
=
0
}
}
}
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment