diff --git a/config/config.go b/config/config.go index 6bc80c96d4..3ee2dc1dfb 100644 --- a/config/config.go +++ b/config/config.go @@ -126,6 +126,8 @@ type Config struct { PrometheusRegisterer prometheus.Registerer DialRanker network.DialRanker + + SwarmOpts []swarm.Option } func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) { @@ -160,7 +162,7 @@ func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swa return nil, err } - opts := make([]swarm.Option, 0, 6) + opts := cfg.SwarmOpts if cfg.Reporter != nil { opts = append(opts, swarm.WithMetrics(cfg.Reporter)) } @@ -176,11 +178,13 @@ func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swa if cfg.MultiaddrResolver != nil { opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver)) } + dialRanker := cfg.DialRanker if dialRanker == nil { dialRanker = swarm.NoDelayDialRanker } opts = append(opts, swarm.WithDialRanker(dialRanker)) + if enableMetrics { opts = append(opts, swarm.WithMetricsTracer(swarm.NewMetricsTracer(swarm.WithRegisterer(cfg.PrometheusRegisterer)))) diff --git a/options.go b/options.go index a124a2e27b..3e072b950c 100644 --- a/options.go +++ b/options.go @@ -23,6 +23,7 @@ import ( "github.com/libp2p/go-libp2p/core/transport" "github.com/libp2p/go-libp2p/p2p/host/autorelay" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + "github.com/libp2p/go-libp2p/p2p/net/swarm" tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader" relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay" "github.com/libp2p/go-libp2p/p2p/protocol/holepunch" @@ -587,3 +588,11 @@ func DialRanker(d network.DialRanker) Option { return nil } } + +// SwarmOpts configures libp2p to use swarm with opts +func SwarmOpts(opts ...swarm.Option) Option { + return func(cfg *Config) error { + cfg.SwarmOpts = opts + return nil + } +} diff --git a/p2p/net/swarm/black_hole_detector.go b/p2p/net/swarm/black_hole_detector.go index 0c415080e0..078b1126c4 100644 --- a/p2p/net/swarm/black_hole_detector.go +++ b/p2p/net/swarm/black_hole_detector.go @@ -241,17 +241,36 @@ func (d *blackHoleDetector) RecordResult(addr ma.Multiaddr, success bool) { } } -func newBlackHoleDetector(detectUDP, detectIPv6 bool, mt MetricsTracer) *blackHoleDetector { +// blackHoleConfig is the config used for black hole detection +type blackHoleConfig struct { + // Enabled enables black hole detection + Enabled bool + // N is the size of the sliding window used to evaluate black hole state + N int + // MinSuccesses is the minimum number of successes out of N required to not + // block requests + MinSuccesses int +} + +func newBlackHoleDetector(udpConfig, ipv6Config blackHoleConfig, mt MetricsTracer) *blackHoleDetector { d := &blackHoleDetector{} - // A black hole is a binary property. On a network if UDP dials are blocked or there is - // no IPv6 connectivity, all dials will fail. So a low success rate of 5 out 100 dials - // is good enough. - if detectUDP { - d.udp = &blackHoleFilter{n: 100, minSuccesses: 5, name: "UDP", metricsTracer: mt} + if udpConfig.Enabled { + d.udp = &blackHoleFilter{ + n: udpConfig.N, + minSuccesses: udpConfig.MinSuccesses, + name: "UDP", + metricsTracer: mt, + } } - if detectIPv6 { - d.ipv6 = &blackHoleFilter{n: 100, minSuccesses: 5, name: "IPv6", metricsTracer: mt} + + if ipv6Config.Enabled { + d.ipv6 = &blackHoleFilter{ + n: ipv6Config.N, + minSuccesses: ipv6Config.MinSuccesses, + name: "IPv6", + metricsTracer: mt, + } } return d } diff --git a/p2p/net/swarm/black_hole_detector_test.go b/p2p/net/swarm/black_hole_detector_test.go index 564fc07767..7b10fc88a6 100644 --- a/p2p/net/swarm/black_hole_detector_test.go +++ b/p2p/net/swarm/black_hole_detector_test.go @@ -75,7 +75,9 @@ func TestBlackHoleFilterSuccessFraction(t *testing.T) { } func TestBlackHoleDetectorInApplicableAddress(t *testing.T) { - bhd := newBlackHoleDetector(true, true, nil) + udpConfig := blackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} + ipv6Config := blackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} + bhd := newBlackHoleDetector(udpConfig, ipv6Config, nil) addrs := []ma.Multiaddr{ ma.StringCast("/ip4/1.2.3.4/tcp/1234"), ma.StringCast("/ip4/1.2.3.4/tcp/1233"), @@ -92,7 +94,8 @@ func TestBlackHoleDetectorInApplicableAddress(t *testing.T) { } func TestBlackHoleDetectorUDPDisabled(t *testing.T) { - bhd := newBlackHoleDetector(false, true, nil) + ipv6Config := blackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} + bhd := newBlackHoleDetector(blackHoleConfig{Enabled: false}, ipv6Config, nil) publicAddr := ma.StringCast("/ip4/1.2.3.4/udp/1234/quic-v1") privAddr := ma.StringCast("/ip4/192.168.1.5/udp/1234/quic-v1") for i := 0; i < 100; i++ { @@ -103,7 +106,8 @@ func TestBlackHoleDetectorUDPDisabled(t *testing.T) { } func TestBlackHoleDetectorIPv6Disabled(t *testing.T) { - bhd := newBlackHoleDetector(true, false, nil) + udpConfig := blackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} + bhd := newBlackHoleDetector(udpConfig, blackHoleConfig{Enabled: false}, nil) publicAddr := ma.StringCast("/ip6/1::1/tcp/1234") privAddr := ma.StringCast("/ip6/::1/tcp/1234") addrs := []ma.Multiaddr{publicAddr, privAddr} diff --git a/p2p/net/swarm/swarm.go b/p2p/net/swarm/swarm.go index c0e8f1cf12..9dfab2d9c0 100644 --- a/p2p/net/swarm/swarm.go +++ b/p2p/net/swarm/swarm.go @@ -108,6 +108,26 @@ func WithDialRanker(d network.DialRanker) Option { } } +// WithUDPBlackHoleConfig configures swarm to use c as the config for UDP black hole detection +// n is the size of the sliding window used to evaluate black hole state +// min is the minimum number of successes out of n required to not block requests +func WithUDPBlackHoleConfig(enabled bool, n, min int) Option { + return func(s *Swarm) error { + s.udpBlackHoleConfig = blackHoleConfig{Enabled: enabled, N: n, MinSuccesses: min} + return nil + } +} + +// WithIPv6BlackHoleConfig configures swarm to use c as the config for IPv6 black hole detection +// n is the size of the sliding window used to evaluate black hole state +// min is the minimum number of successes out of n required to not block requests +func WithIPv6BlackHoleConfig(enabled bool, n, min int) Option { + return func(s *Swarm) error { + s.ipv6BlackHoleConfig = blackHoleConfig{Enabled: enabled, N: n, MinSuccesses: min} + return nil + } +} + // Swarm is a connection muxer, allowing connections to other peers to // be opened and closed, while still using the same Chan for all // communication. The Chan sends/receives Messages, which note the @@ -174,7 +194,9 @@ type Swarm struct { dialRanker network.DialRanker - bhd *blackHoleDetector + udpBlackHoleConfig blackHoleConfig + ipv6BlackHoleConfig blackHoleConfig + bhd *blackHoleDetector } // NewSwarm constructs a Swarm. @@ -194,6 +216,12 @@ func NewSwarm(local peer.ID, peers peerstore.Peerstore, eventBus event.Bus, opts dialTimeoutLocal: defaultDialTimeoutLocal, maResolver: madns.DefaultResolver, dialRanker: DefaultDialRanker, + + // A black hole is a binary property. On a network if UDP dials are blocked or there is + // no IPv6 connectivity, all dials will fail. So a low success rate of 5 out 100 dials + // is good enough. + udpBlackHoleConfig: blackHoleConfig{Enabled: true, N: 100, MinSuccesses: 5}, + ipv6BlackHoleConfig: blackHoleConfig{Enabled: true, N: 100, MinSuccesses: 5}, } s.conns.m = make(map[peer.ID][]*Conn) @@ -215,7 +243,7 @@ func NewSwarm(local peer.ID, peers peerstore.Peerstore, eventBus event.Bus, opts s.limiter = newDialLimiter(s.dialAddr) s.backf.init(s.ctx) - s.bhd = newBlackHoleDetector(true, true, s.metricsTracer) + s.bhd = newBlackHoleDetector(s.udpBlackHoleConfig, s.ipv6BlackHoleConfig, s.metricsTracer) return s, nil }