Skip to content

Commit 62cbac4

Browse files
committed
bitswap/server/internal/decision: add filtering on CIDs
- Ignore cids that are too big. - Kill connection for peers that are using inline CIDs.
1 parent 9cb5cb5 commit 62cbac4

File tree

5 files changed

+71
-4
lines changed

5 files changed

+71
-4
lines changed

bitswap/internal/defaults/defaults.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package defaults
22

33
import (
4+
"encoding/binary"
45
"time"
56
)
67

@@ -27,4 +28,9 @@ const (
2728

2829
// Maximum size of the wantlist we are willing to keep in memory.
2930
MaxQueuedWantlistEntiresPerPeer = 1024
31+
32+
// Copied from github.com/ipfs/go-verifcid#maximumHashLength
33+
// FIXME: expose this in go-verifcid.
34+
MaximumHashLength = 128
35+
MaximumAllowedCid = binary.MaxVarintLen64*4 + MaximumHashLength
3036
)

bitswap/network/ipfs_impl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ func (bsnet *impl) ConnectTo(ctx context.Context, p peer.ID) error {
370370
}
371371

372372
func (bsnet *impl) DisconnectFrom(ctx context.Context, p peer.ID) error {
373-
panic("Not implemented: DisconnectFrom() is only used by tests")
373+
return bsnet.host.Network().ClosePeer(p)
374374
}
375375

376376
// FindProvidersAsync returns a channel of providers for the given key.

bitswap/options.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ func MaxQueuedWantlistEntriesPerPeer(count uint) Option {
3333
return Option{server.MaxQueuedWantlistEntriesPerPeer(count)}
3434
}
3535

36+
// MaxCidSize only affects the server.
37+
// If it is 0 no limit is applied.
38+
func MaxCidSize(n uint) Option {
39+
return Option{server.MaxCidSize(n)}
40+
}
41+
3642
func TaskWorkerCount(count int) Option {
3743
return Option{server.TaskWorkerCount(count)}
3844
}

bitswap/server/internal/decision/engine.go

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/ipfs/go-peertaskqueue/peertracker"
2626
process "github.com/jbenet/goprocess"
2727
"github.com/libp2p/go-libp2p/core/peer"
28+
mh "github.com/multiformats/go-multihash"
2829
)
2930

3031
// TODO consider taking responsibility for other types of requests. For
@@ -187,6 +188,7 @@ type Engine struct {
187188
maxOutstandingBytesPerPeer int
188189

189190
maxQueuedWantlistEntriesPerPeer uint
191+
maxCidSize uint
190192
}
191193

192194
// TaskInfo represents the details of a request from a peer.
@@ -272,13 +274,20 @@ func WithMaxOutstandingBytesPerPeer(count int) Option {
272274

273275
// WithMaxQueuedWantlistEntriesPerPeer limits how much individual entries each peer is allowed to send.
274276
// If a peer send us more than this we will truncate newest entries.
275-
// It defaults to DefaultMaxQueuedWantlistEntiresPerPeer.
276277
func WithMaxQueuedWantlistEntriesPerPeer(count uint) Option {
277278
return func(e *Engine) {
278279
e.maxQueuedWantlistEntriesPerPeer = count
279280
}
280281
}
281282

283+
// WithMaxQueuedWantlistEntriesPerPeer limits how much individual entries each peer is allowed to send.
284+
// If a peer send us more than this we will truncate newest entries.
285+
func WithMaxCidSize(n uint) Option {
286+
return func(e *Engine) {
287+
e.maxCidSize = n
288+
}
289+
}
290+
282291
func WithSetSendDontHave(send bool) Option {
283292
return func(e *Engine) {
284293
e.sendDontHaves = send
@@ -357,6 +366,7 @@ func newEngine(
357366
tagQueued: fmt.Sprintf(tagFormat, "queued", uuid.New().String()),
358367
tagUseful: fmt.Sprintf(tagFormat, "useful", uuid.New().String()),
359368
maxQueuedWantlistEntriesPerPeer: defaults.MaxQueuedWantlistEntiresPerPeer,
369+
maxCidSize: defaults.MaximumAllowedCid,
360370
}
361371

362372
for _, opt := range opts {
@@ -617,7 +627,7 @@ func (e *Engine) Peers() []peer.ID {
617627
// MessageReceived is called when a message is received from a remote peer.
618628
// For each item in the wantlist, add a want-have or want-block entry to the
619629
// request queue (this is later popped off by the workerTasks)
620-
func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwapMessage) {
630+
func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwapMessage) (mustKillConnection bool) {
621631
entries := m.Wantlist()
622632

623633
if len(entries) > 0 {
@@ -676,10 +686,40 @@ func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwap
676686
wants = wants[:available]
677687
}
678688

689+
filteredWants := wants[:0] // shift inplace
690+
679691
for _, entry := range wants {
692+
if entry.Cid.Prefix().MhType == mh.IDENTITY {
693+
// This is a truely broken client, let's kill the connection.
694+
e.lock.Unlock()
695+
log.Warnw("peer wants an identity CID", "local", e.self, "remote", p)
696+
return true
697+
}
698+
if e.maxCidSize != 0 && uint(entry.Cid.ByteLen()) > e.maxCidSize {
699+
// Ignore requests about CIDs that big.
700+
continue
701+
}
702+
680703
e.peerLedger.Wants(p, entry.Entry)
704+
filteredWants = append(filteredWants, entry)
705+
}
706+
clear := wants[len(filteredWants):]
707+
for i := range clear {
708+
clear[i] = bsmsg.Entry{} // early GC
681709
}
710+
wants = filteredWants
682711
for _, entry := range cancels {
712+
if entry.Cid.Prefix().MhType == mh.IDENTITY {
713+
// This is a truely broken client, let's kill the connection.
714+
e.lock.Unlock()
715+
log.Warnw("peer canceled an identity CID", "local", e.self, "remote", p)
716+
return true
717+
}
718+
if e.maxCidSize != 0 && uint(entry.Cid.ByteLen()) > e.maxCidSize {
719+
// Ignore requests about CIDs that big.
720+
continue
721+
}
722+
683723
log.Debugw("Bitswap engine <- cancel", "local", e.self, "from", p, "cid", entry.Cid)
684724
if e.peerLedger.CancelWant(p, entry.Cid) {
685725
e.peerRequestQueue.Remove(entry.Cid, p)
@@ -765,6 +805,7 @@ func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwap
765805
e.peerRequestQueue.PushTasksTruncated(e.maxQueuedWantlistEntriesPerPeer, p, activeEntries...)
766806
e.updateMetrics()
767807
}
808+
return false
768809
}
769810

770811
// Split the want-have / want-block entries from the cancel entries

bitswap/server/server.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,17 @@ func MaxQueuedWantlistEntriesPerPeer(count uint) Option {
220220
}
221221
}
222222

223+
// MaxCidSize limits how big CIDs we are willing to serve.
224+
// We will ignore CIDs over this limit.
225+
// It defaults to [defaults.MaxCidSize].
226+
// If it is 0 no limit is applied.
227+
func MaxCidSize(n uint) Option {
228+
o := decision.WithMaxCidSize(n)
229+
return func(bs *Server) {
230+
bs.engineOptions = append(bs.engineOptions, o)
231+
}
232+
}
233+
223234
// HasBlockBufferSize configure how big the new blocks buffer should be.
224235
func HasBlockBufferSize(count int) Option {
225236
if count < 0 {
@@ -511,7 +522,10 @@ func (bs *Server) provideWorker(px process.Process) {
511522
func (bs *Server) ReceiveMessage(ctx context.Context, p peer.ID, incoming message.BitSwapMessage) {
512523
// This call records changes to wantlists, blocks received,
513524
// and number of bytes transfered.
514-
bs.engine.MessageReceived(ctx, p, incoming)
525+
mustKillConnection := bs.engine.MessageReceived(ctx, p, incoming)
526+
if mustKillConnection {
527+
bs.network.DisconnectFrom(ctx, p)
528+
}
515529
// TODO: this is bad, and could be easily abused.
516530
// Should only track *useful* messages in ledger
517531

0 commit comments

Comments
 (0)