From 1dafcf24f14bd8af903d879f1d8c92d4462670fa Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Sun, 16 Feb 2020 21:51:18 -0800 Subject: [PATCH 1/2] basic gossip spam protection logic --- chain/sub/incoming.go | 104 ++++++++++++++++++++++++++++++++++----- go.mod | 8 +-- go.sum | 22 +++++++++ node/modules/services.go | 34 +++++++++++-- 4 files changed, 148 insertions(+), 20 deletions(-) diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index fc554f4b3..6ee41aabf 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -4,8 +4,11 @@ import ( "context" "time" + lru "github.com/hashicorp/golang-lru" + "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" connmgr "github.com/libp2p/go-libp2p-core/connmgr" + peer "github.com/libp2p/go-libp2p-peer" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/filecoin-project/lotus/build" @@ -28,15 +31,9 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha continue } - blk, err := types.DecodeBlockMsg(msg.GetData()) - if err != nil { - log.Error("got invalid block over pubsub: ", err) - continue - } - - if len(blk.BlsMessages)+len(blk.SecpkMessages) > build.BlockMessageLimit { - log.Warnf("received block with too many messages over pubsub") - continue + blk, ok := msg.ValidatorData.(*types.BlockMsg) + if !ok { + log.Warnf("pubsub block validator passed on wrong type: %#v", msg.ValidatorData) } go func() { @@ -73,6 +70,89 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha } } +type BlockValidator struct { + peers *lru.TwoQueueCache + + killThresh int + + recvBlocks *blockReceiptCache + + blacklist func(peer.ID) +} + +func NewBlockValidator(blacklist func(peer.ID)) *BlockValidator { + p, _ := lru.New2Q(4096) + return &BlockValidator{ + peers: p, + killThresh: 5, + blacklist: blacklist, + recvBlocks: newBlockReceiptCache(), + } +} + +func (bv *BlockValidator) flagPeer(p peer.ID) { + v, ok := bv.peers.Get(p) + if !ok { + bv.peers.Add(p, int(1)) + return + } + + val := v.(int) + + if val >= bv.killThresh { + bv.blacklist(p) + } + + bv.peers.Add(p, v.(int)+1) +} + +func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub.Message) bool { + blk, err := types.DecodeBlockMsg(msg.GetData()) + if err != nil { + log.Error("got invalid block over pubsub: ", err) + bv.flagPeer(pid) + return false + } + + if len(blk.BlsMessages)+len(blk.SecpkMessages) > build.BlockMessageLimit { + log.Warnf("received block with too many messages over pubsub") + bv.flagPeer(pid) + return false + } + + if bv.recvBlocks.add(blk.Header.Cid()) > 0 { + // TODO: once these changes propagate to the network, we can consider + // dropping peers who send us the same block multiple times + return false + } + + msg.ValidatorData = blk + return true +} + +type blockReceiptCache struct { + blocks *lru.TwoQueueCache +} + +func newBlockReceiptCache() *blockReceiptCache { + c, _ := lru.New2Q(8192) + + return &blockReceiptCache{ + blocks: c, + } +} + +func (brc *blockReceiptCache) add(bcid cid.Cid) int { + val, ok := brc.blocks.Get(bcid) + if !ok { + brc.blocks.Add(bcid, int(1)) + return 0 + } + + brc.blocks.Add(bcid, val.(int)+1) + return val.(int) +} + func HandleIncomingMessages(ctx context.Context, mpool *messagepool.MessagePool, msub *pubsub.Subscription) { for { msg, err := msub.Next(ctx) @@ -85,9 +165,9 @@ func HandleIncomingMessages(ctx context.Context, mpool *messagepool.MessagePool, continue } - m, err := types.DecodeSignedMessage(msg.GetData()) - if err != nil { - log.Errorf("got incorrectly formatted Message: %s", err) + m, ok := msg.ValidatorData.(*types.SignedMessage) + if !ok { + log.Errorf("message validator func passed on wrong type: %#v", msg.ValidatorData) continue } diff --git a/go.mod b/go.mod index 50d7d8fb7..718ba9e43 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/gorilla/mux v1.7.3 github.com/gorilla/websocket v1.4.1 github.com/hashicorp/go-multierror v1.0.0 - github.com/hashicorp/golang-lru v0.5.3 + github.com/hashicorp/golang-lru v0.5.4 github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e github.com/ipfs/go-bitswap v0.1.8 github.com/ipfs/go-block-format v0.0.2 @@ -58,13 +58,13 @@ require ( github.com/libp2p/go-libp2p v0.4.2 github.com/libp2p/go-libp2p-circuit v0.1.4 github.com/libp2p/go-libp2p-connmgr v0.1.0 - github.com/libp2p/go-libp2p-core v0.2.4 + github.com/libp2p/go-libp2p-core v0.3.0 github.com/libp2p/go-libp2p-discovery v0.2.0 github.com/libp2p/go-libp2p-kad-dht v0.1.1 github.com/libp2p/go-libp2p-mplex v0.2.1 github.com/libp2p/go-libp2p-peer v0.2.0 github.com/libp2p/go-libp2p-peerstore v0.1.4 - github.com/libp2p/go-libp2p-pubsub v0.2.3 + github.com/libp2p/go-libp2p-pubsub v0.2.6 github.com/libp2p/go-libp2p-quic-transport v0.1.1 github.com/libp2p/go-libp2p-record v0.1.1 github.com/libp2p/go-libp2p-routing-helpers v0.1.0 @@ -76,7 +76,7 @@ require ( github.com/minio/sha256-simd v0.1.1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.0.3 - github.com/multiformats/go-multiaddr v0.1.1 + github.com/multiformats/go-multiaddr v0.2.0 github.com/multiformats/go-multiaddr-dns v0.2.0 github.com/multiformats/go-multiaddr-net v0.1.1 github.com/multiformats/go-multihash v0.0.10 diff --git a/go.sum b/go.sum index 29beb8ca5..ed8807ca5 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcug github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5njHPn0kLAsykn6mJGz7rnmW5W0= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -185,6 +187,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -368,6 +372,10 @@ github.com/libp2p/go-eventbus v0.1.0 h1:mlawomSAjjkk97QnYiEmHsLu7E136+2oCWSHRUvM github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= github.com/libp2p/go-flow-metrics v0.0.1 h1:0gxuFd2GuK7IIP5pKljLwps6TvcuYgvG7Atqi3INF5s= github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= +github.com/libp2p/go-flow-metrics v0.0.2 h1:U5TvqfoyR6GVRM+bC15Ux1ltar1kbj6Zw6xOVR02CZs= +github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= +github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= +github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-libp2p v0.0.30/go.mod h1:XWT8FGHlhptAv1+3V/+J5mEpzyui/5bvFsNuWYs611A= github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= @@ -401,6 +409,10 @@ github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= github.com/libp2p/go-libp2p-core v0.2.4 h1:Et6ykkTwI6PU44tr8qUF9k43vP0aduMNniShAbUJJw8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= +github.com/libp2p/go-libp2p-core v0.2.5 h1:iP1PIiIrlRrGbE1fYq2918yBc5NlCH3pFuIPSWU9hds= +github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= +github.com/libp2p/go-libp2p-core v0.3.0 h1:F7PqduvrztDtFsAa/bcheQ3azmNo+Nq7m8hQY5GiUW8= +github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= @@ -450,6 +462,10 @@ github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1 github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk= github.com/libp2p/go-libp2p-pubsub v0.2.3 h1:qJRnRnM7Z4xnHb4i6EBb3DKQXRPgtFWlKP4AmfJudLQ= github.com/libp2p/go-libp2p-pubsub v0.2.3/go.mod h1:Jscj3fk23R5mCrOwb625xjVs5ZEyTZcx/OlTwMDqU+g= +github.com/libp2p/go-libp2p-pubsub v0.2.5 h1:tPKbkjAUI0xLGN3KKTKKy9TQEviVfrP++zJgH5Muke4= +github.com/libp2p/go-libp2p-pubsub v0.2.5/go.mod h1:9Q2RRq8ofXkoewORcyVlgUFDKLKw7BuYSlJVWRcVk3Y= +github.com/libp2p/go-libp2p-pubsub v0.2.6 h1:ypZaukCFrtD8cNeeb9nnWG4MD2Y1T0p22aQ+f7FKJig= +github.com/libp2p/go-libp2p-pubsub v0.2.6/go.mod h1:5jEp7R3ItQ0pgcEMrPZYE9DQTg/H3CTc7Mu1j2G4Y5o= github.com/libp2p/go-libp2p-quic-transport v0.1.1 h1:MFMJzvsxIEDEVKzO89BnB/FgvMj9WI4GDGUW2ArDPUA= github.com/libp2p/go-libp2p-quic-transport v0.1.1/go.mod h1:wqG/jzhF3Pu2NrhJEvE+IE0NTHNXslOPn9JQzyCAxzU= github.com/libp2p/go-libp2p-record v0.0.1/go.mod h1:grzqg263Rug/sRex85QrDOLntdFAymLDLm7lxMgU79Q= @@ -510,6 +526,8 @@ github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/ github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= github.com/libp2p/go-openssl v0.0.3 h1:wjlG7HvQkt4Fq4cfH33Ivpwp0omaElYEi9z26qaIkIk= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg= +github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4= @@ -579,6 +597,8 @@ github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lg github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.1 h1:rVAztJYMhCQ7vEFr8FvxW3mS+HF2eY/oPbOMeS0ZDnE= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90= +github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= @@ -604,6 +624,8 @@ github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wS github.com/multiformats/go-multistream v0.0.4/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.0 h1:UpO6jrsjqs46mqAK3n6wKRYFhugss9ArzbyUzU+4wkQ= github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/multiformats/go-multistream v0.1.1 h1:JlAdpIFhBhGRLxe9W6Om0w++Gd6KMWoFPZL/dEnm9nI= +github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.2 h1:6sUvyh2YHpJCb8RZ6eYzj6iJQ4+chWYmyIHxszqlPTA= github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= diff --git a/node/modules/services.go b/node/modules/services.go index 33f2733b5..4d4195879 100644 --- a/node/modules/services.go +++ b/node/modules/services.go @@ -5,6 +5,7 @@ import ( "github.com/libp2p/go-libp2p-core/host" inet "github.com/libp2p/go-libp2p-core/network" + peer "github.com/libp2p/go-libp2p-peer" pubsub "github.com/libp2p/go-libp2p-pubsub" "go.uber.org/fx" @@ -15,12 +16,16 @@ import ( "github.com/filecoin-project/lotus/chain/blocksync" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/sub" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/hello" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/modules/helpers" "github.com/filecoin-project/lotus/peermgr" ) +const BlocksTopic = "/fil/blocks" +const MessagesTopic = "/fil/messages" + func RunHello(mctx helpers.MetricsCtx, lc fx.Lifecycle, h host.Host, svc *hello.Service) { h.SetStreamHandler(hello.ProtocolID, svc.HandleStream) @@ -45,25 +50,46 @@ func RunBlockSync(h host.Host, svc *blocksync.BlockSyncService) { h.SetStreamHandler(blocksync.BlockSyncProtocolID, svc.HandleStream) } -func HandleIncomingBlocks(mctx helpers.MetricsCtx, lc fx.Lifecycle, pubsub *pubsub.PubSub, s *chain.Syncer, h host.Host) { +func HandleIncomingBlocks(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, s *chain.Syncer, h host.Host) { ctx := helpers.LifecycleCtx(mctx, lc) - blocksub, err := pubsub.Subscribe("/fil/blocks") + blocksub, err := ps.Subscribe(BlocksTopic) if err != nil { panic(err) } + v := sub.NewBlockValidator(ps.BlacklistPeer) + + if err := ps.RegisterTopicValidator(BlocksTopic, v.Validate); err != nil { + panic(err) + } + go sub.HandleIncomingBlocks(ctx, blocksub, s, h.ConnManager()) } -func HandleIncomingMessages(mctx helpers.MetricsCtx, lc fx.Lifecycle, pubsub *pubsub.PubSub, mpool *messagepool.MessagePool) { +func HandleIncomingMessages(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, mpool *messagepool.MessagePool) { ctx := helpers.LifecycleCtx(mctx, lc) - msgsub, err := pubsub.Subscribe("/fil/messages") + msgsub, err := ps.Subscribe(MessagesTopic) if err != nil { panic(err) } + v := func(ctx context.Context, pid peer.ID, msg *pubsub.Message) bool { + m, err := types.DecodeSignedMessage(msg.GetData()) + if err != nil { + log.Errorf("got incorrectly formatted Message: %s", err) + return false + } + + msg.ValidatorData = m + return true + } + + if err := ps.RegisterTopicValidator(MessagesTopic, v); err != nil { + panic(err) + } + go sub.HandleIncomingMessages(ctx, mpool, msgsub) } From 4f6ec225cf818efb74b7155b49a6bc5afdf02134 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Tue, 18 Feb 2020 09:20:17 -0800 Subject: [PATCH 2/2] mark peer for disconnection in connmgr --- node/modules/services.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/modules/services.go b/node/modules/services.go index 4d4195879..0ae6d98f0 100644 --- a/node/modules/services.go +++ b/node/modules/services.go @@ -58,7 +58,10 @@ func HandleIncomingBlocks(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.P panic(err) } - v := sub.NewBlockValidator(ps.BlacklistPeer) + v := sub.NewBlockValidator(func(p peer.ID) { + ps.BlacklistPeer(p) + h.ConnManager().TagPeer(p, "badblock", -1000) + }) if err := ps.RegisterTopicValidator(BlocksTopic, v.Validate); err != nil { panic(err)