|
@ -12,10 +12,15 @@ import ( |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
const ( |
|
|
const ( |
|
|
PexChannel = byte(0x00) |
|
|
|
|
|
|
|
|
PexChannel = byte(0x00) |
|
|
|
|
|
// period to ensure peers connected
|
|
|
defaultEnsurePeersPeriod = 30 * time.Second |
|
|
defaultEnsurePeersPeriod = 30 * time.Second |
|
|
minNumOutboundPeers = 10 |
|
|
minNumOutboundPeers = 10 |
|
|
maxPexMessageSize = 1048576 // 1MB
|
|
|
maxPexMessageSize = 1048576 // 1MB
|
|
|
|
|
|
|
|
|
|
|
|
// maximum messages one peer can send to us during `msgCountByPeerFlushInterval`
|
|
|
|
|
|
defaultMaxMsgCountByPeer = 1000 |
|
|
|
|
|
msgCountByPeerFlushInterval = 1 * time.Hour |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
// PEXReactor handles PEX (peer exchange) and ensures that an
|
|
|
// PEXReactor handles PEX (peer exchange) and ensures that an
|
|
@ -26,12 +31,18 @@ type PEXReactor struct { |
|
|
sw *Switch |
|
|
sw *Switch |
|
|
book *AddrBook |
|
|
book *AddrBook |
|
|
ensurePeersPeriod time.Duration |
|
|
ensurePeersPeriod time.Duration |
|
|
|
|
|
|
|
|
|
|
|
// tracks message count by peer, so we can prevent abuse
|
|
|
|
|
|
msgCountByPeer map[string]uint16 |
|
|
|
|
|
maxMsgCountByPeer uint16 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func NewPEXReactor(b *AddrBook) *PEXReactor { |
|
|
func NewPEXReactor(b *AddrBook) *PEXReactor { |
|
|
r := &PEXReactor{ |
|
|
r := &PEXReactor{ |
|
|
book: b, |
|
|
book: b, |
|
|
ensurePeersPeriod: defaultEnsurePeersPeriod, |
|
|
ensurePeersPeriod: defaultEnsurePeersPeriod, |
|
|
|
|
|
msgCountByPeer: make(map[string]uint16), |
|
|
|
|
|
maxMsgCountByPeer: defaultMaxMsgCountByPeer, |
|
|
} |
|
|
} |
|
|
r.BaseReactor = *NewBaseReactor(log, "PEXReactor", r) |
|
|
r.BaseReactor = *NewBaseReactor(log, "PEXReactor", r) |
|
|
return r |
|
|
return r |
|
@ -41,6 +52,7 @@ func (r *PEXReactor) OnStart() error { |
|
|
r.BaseReactor.OnStart() |
|
|
r.BaseReactor.OnStart() |
|
|
r.book.Start() |
|
|
r.book.Start() |
|
|
go r.ensurePeersRoutine() |
|
|
go r.ensurePeersRoutine() |
|
|
|
|
|
go r.flushMsgCountByPeer() |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -89,24 +101,29 @@ func (r *PEXReactor) RemovePeer(p *Peer, reason interface{}) { |
|
|
|
|
|
|
|
|
// Receive implements Reactor by handling incoming PEX messages.
|
|
|
// Receive implements Reactor by handling incoming PEX messages.
|
|
|
func (r *PEXReactor) Receive(chID byte, src *Peer, msgBytes []byte) { |
|
|
func (r *PEXReactor) Receive(chID byte, src *Peer, msgBytes []byte) { |
|
|
|
|
|
srcAddr := src.Connection().RemoteAddress |
|
|
|
|
|
srcAddrStr := srcAddr.String() |
|
|
|
|
|
r.msgCountByPeer[srcAddrStr]++ |
|
|
|
|
|
if r.ReachedMaxMsgCountForPeer(srcAddrStr) { |
|
|
|
|
|
log.Warn("Maximum number of messages reached for peer", "peer", srcAddrStr) |
|
|
|
|
|
// TODO remove src from peers?
|
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
_, msg, err := DecodeMessage(msgBytes) |
|
|
_, msg, err := DecodeMessage(msgBytes) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
log.Warn("Error decoding message", "error", err) |
|
|
log.Warn("Error decoding message", "error", err) |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
log.Notice("Received message", "msg", msg) |
|
|
log.Notice("Received message", "msg", msg) |
|
|
|
|
|
|
|
|
switch msg := msg.(type) { |
|
|
switch msg := msg.(type) { |
|
|
case *pexRequestMessage: |
|
|
case *pexRequestMessage: |
|
|
// src requested some peers.
|
|
|
// src requested some peers.
|
|
|
// TODO: prevent abuse.
|
|
|
|
|
|
r.SendAddrs(src, r.book.GetSelection()) |
|
|
r.SendAddrs(src, r.book.GetSelection()) |
|
|
case *pexAddrsMessage: |
|
|
case *pexAddrsMessage: |
|
|
// We received some peer addresses from src.
|
|
|
// We received some peer addresses from src.
|
|
|
// TODO: prevent abuse.
|
|
|
|
|
|
// (We don't want to get spammed with bad peers)
|
|
|
// (We don't want to get spammed with bad peers)
|
|
|
srcAddr := src.Connection().RemoteAddress |
|
|
|
|
|
for _, addr := range msg.Addrs { |
|
|
for _, addr := range msg.Addrs { |
|
|
if addr != nil { |
|
|
if addr != nil { |
|
|
r.book.AddAddress(addr, srcAddr) |
|
|
r.book.AddAddress(addr, srcAddr) |
|
@ -132,6 +149,17 @@ func (r *PEXReactor) SetEnsurePeersPeriod(d time.Duration) { |
|
|
r.ensurePeersPeriod = d |
|
|
r.ensurePeersPeriod = d |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// SetMaxMsgCountByPeer sets maximum messages one peer can send to us during 'msgCountByPeerFlushInterval'.
|
|
|
|
|
|
func (r *PEXReactor) SetMaxMsgCountByPeer(v uint16) { |
|
|
|
|
|
r.maxMsgCountByPeer = v |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ReachedMaxMsgCountForPeer returns true if we received too many
|
|
|
|
|
|
// messages from peer with address `addr`.
|
|
|
|
|
|
func (r *PEXReactor) ReachedMaxMsgCountForPeer(addr string) bool { |
|
|
|
|
|
return r.msgCountByPeer[addr] >= r.maxMsgCountByPeer |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Ensures that sufficient peers are connected. (continuous)
|
|
|
// Ensures that sufficient peers are connected. (continuous)
|
|
|
func (r *PEXReactor) ensurePeersRoutine() { |
|
|
func (r *PEXReactor) ensurePeersRoutine() { |
|
|
// Randomize when routine starts
|
|
|
// Randomize when routine starts
|
|
@ -143,17 +171,16 @@ func (r *PEXReactor) ensurePeersRoutine() { |
|
|
|
|
|
|
|
|
// fire periodically
|
|
|
// fire periodically
|
|
|
ticker := time.NewTicker(r.ensurePeersPeriod) |
|
|
ticker := time.NewTicker(r.ensurePeersPeriod) |
|
|
FOR_LOOP: |
|
|
|
|
|
|
|
|
|
|
|
for { |
|
|
for { |
|
|
select { |
|
|
select { |
|
|
case <-ticker.C: |
|
|
case <-ticker.C: |
|
|
r.ensurePeers() |
|
|
r.ensurePeers() |
|
|
case <-r.Quit: |
|
|
case <-r.Quit: |
|
|
break FOR_LOOP |
|
|
|
|
|
|
|
|
ticker.Stop() |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
ticker.Stop() |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// ensurePeers ensures that sufficient peers are connected. (once)
|
|
|
// ensurePeers ensures that sufficient peers are connected. (once)
|
|
@ -222,6 +249,20 @@ func (r *PEXReactor) ensurePeers() { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (r *PEXReactor) flushMsgCountByPeer() { |
|
|
|
|
|
ticker := time.NewTicker(msgCountByPeerFlushInterval) |
|
|
|
|
|
|
|
|
|
|
|
for { |
|
|
|
|
|
select { |
|
|
|
|
|
case <-ticker.C: |
|
|
|
|
|
r.msgCountByPeer = make(map[string]uint16) |
|
|
|
|
|
case <-r.Quit: |
|
|
|
|
|
ticker.Stop() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// Messages
|
|
|
// Messages
|
|
|
|
|
|
|
|
|