mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-28 20:21:47 +00:00
prevent abuse from peers
This commit is contained in:
committed by
Anton Kaliaev
parent
47df1fb7d4
commit
873d34157d
@ -13,9 +13,14 @@ 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()
|
ticker.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -102,6 +102,21 @@ func TestPEXReactorReceive(t *testing.T) {
|
|||||||
r.Receive(PexChannel, peer, msg)
|
r.Receive(PexChannel, peer, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPEXReactorAbuseFromPeer(t *testing.T) {
|
||||||
|
book := NewAddrBook(createTempFileName("addrbook"), true)
|
||||||
|
r := NewPEXReactor(book)
|
||||||
|
r.SetMaxMsgCountByPeer(5)
|
||||||
|
|
||||||
|
peer := createRandomPeer(false)
|
||||||
|
|
||||||
|
msg := wire.BinaryBytes(struct{ PexMessage }{&pexRequestMessage{}})
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
r.Receive(PexChannel, peer, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, r.ReachedMaxMsgCountForPeer(peer.ListenAddr))
|
||||||
|
}
|
||||||
|
|
||||||
func createRandomPeer(outbound bool) *Peer {
|
func createRandomPeer(outbound bool) *Peer {
|
||||||
addr := Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256)
|
addr := Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256)
|
||||||
return &Peer{
|
return &Peer{
|
||||||
|
Reference in New Issue
Block a user