From 63f56d3ca246d5708e5e26daec05a642d66d1e77 Mon Sep 17 00:00:00 2001 From: sukun Date: Fri, 30 Jun 2023 00:01:51 +0530 Subject: [PATCH 1/2] swarm: make black hole detection configurable --- config/config.go | 11 +++++++ options.go | 23 +++++++++++++++ p2p/net/swarm/black_hole_detector.go | 35 +++++++++++++++++------ p2p/net/swarm/black_hole_detector_test.go | 10 +++++-- p2p/net/swarm/swarm.go | 34 ++++++++++++++++++++-- 5 files changed, 100 insertions(+), 13 deletions(-) diff --git a/config/config.go b/config/config.go index 6bc80c96d4..138729a44c 100644 --- a/config/config.go +++ b/config/config.go @@ -126,6 +126,9 @@ type Config struct { PrometheusRegisterer prometheus.Registerer DialRanker network.DialRanker + + UDPBlackHoleConfig *swarm.BlackHoleConfig + IPv6BlackHoleConfig *swarm.BlackHoleConfig } func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) { @@ -176,11 +179,19 @@ 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 cfg.UDPBlackHoleConfig != nil { + opts = append(opts, swarm.WithUDPBlackHoleConfig(cfg.UDPBlackHoleConfig)) + } + if cfg.IPv6BlackHoleConfig != nil { + opts = append(opts, swarm.WithIPv6BlackHoleConfig(cfg.IPv6BlackHoleConfig)) + } if enableMetrics { opts = append(opts, swarm.WithMetricsTracer(swarm.NewMetricsTracer(swarm.WithRegisterer(cfg.PrometheusRegisterer)))) diff --git a/options.go b/options.go index a124a2e27b..2c5a24a298 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,25 @@ func DialRanker(d network.DialRanker) Option { return nil } } + +// UDPBlackHoleConfig configures libp2p to use c as the config for UDP black hole detection +func UDPBlackHoleConfig(c *swarm.BlackHoleConfig) Option { + return func(cfg *Config) error { + if c == nil { + return errors.New("udp black hole config cannot be nil") + } + cfg.UDPBlackHoleConfig = c + return nil + } +} + +// IPv6BlackHoleConfig configures libp2p to use c as the config for IPv6 black hole detection +func IPv6BlackHoleConfig(c *swarm.BlackHoleConfig) Option { + return func(cfg *Config) error { + if c == nil { + return errors.New("ipv6 black hole config cannot be nil") + } + cfg.IPv6BlackHoleConfig = c + return nil + } +} diff --git a/p2p/net/swarm/black_hole_detector.go b/p2p/net/swarm/black_hole_detector.go index 0c415080e0..990b17d7bd 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 != nil && 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 != nil && 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..bb8fc8699b 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(nil, 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, nil, 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..0032386aae 100644 --- a/p2p/net/swarm/swarm.go +++ b/p2p/net/swarm/swarm.go @@ -108,6 +108,28 @@ func WithDialRanker(d network.DialRanker) Option { } } +// WithUDPBlackHoleConfig configures swarm to use c as the config for UDP black hole detection +func WithUDPBlackHoleConfig(c *BlackHoleConfig) Option { + return func(s *Swarm) error { + if c == nil { + return errors.New("udp black hole config cannot be nil") + } + s.udpBlackHoleConfig = c + return nil + } +} + +// WithIPv6BlackHoleConfig configures swarm to use c as the config for IPv6 black hole detection +func WithIPv6BlackHoleConfig(c *BlackHoleConfig) Option { + return func(s *Swarm) error { + if c == nil { + return errors.New("ipv6 black hole config cannot be nil") + } + s.ipv6BlackHoleConfig = c + 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 +196,9 @@ type Swarm struct { dialRanker network.DialRanker - bhd *blackHoleDetector + udpBlackHoleConfig *BlackHoleConfig + ipv6BlackHoleConfig *BlackHoleConfig + bhd *blackHoleDetector } // NewSwarm constructs a Swarm. @@ -194,6 +218,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: 10, MinSuccesses: 5}, } s.conns.m = make(map[peer.ID][]*Conn) @@ -215,7 +245,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 } From 74594e9a071029b637ebd63d1a71e24de060e1b9 Mon Sep 17 00:00:00 2001 From: sukun Date: Fri, 30 Jun 2023 02:11:31 +0530 Subject: [PATCH 2/2] address review comments --- config/config.go | 11 ++-------- options.go | 20 +++-------------- p2p/net/swarm/black_hole_detector.go | 10 ++++----- p2p/net/swarm/black_hole_detector_test.go | 12 +++++------ p2p/net/swarm/swarm.go | 26 +++++++++++------------ 5 files changed, 28 insertions(+), 51 deletions(-) diff --git a/config/config.go b/config/config.go index 138729a44c..3ee2dc1dfb 100644 --- a/config/config.go +++ b/config/config.go @@ -127,8 +127,7 @@ type Config struct { DialRanker network.DialRanker - UDPBlackHoleConfig *swarm.BlackHoleConfig - IPv6BlackHoleConfig *swarm.BlackHoleConfig + SwarmOpts []swarm.Option } func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) { @@ -163,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)) } @@ -186,12 +185,6 @@ func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swa } opts = append(opts, swarm.WithDialRanker(dialRanker)) - if cfg.UDPBlackHoleConfig != nil { - opts = append(opts, swarm.WithUDPBlackHoleConfig(cfg.UDPBlackHoleConfig)) - } - if cfg.IPv6BlackHoleConfig != nil { - opts = append(opts, swarm.WithIPv6BlackHoleConfig(cfg.IPv6BlackHoleConfig)) - } if enableMetrics { opts = append(opts, swarm.WithMetricsTracer(swarm.NewMetricsTracer(swarm.WithRegisterer(cfg.PrometheusRegisterer)))) diff --git a/options.go b/options.go index 2c5a24a298..3e072b950c 100644 --- a/options.go +++ b/options.go @@ -589,24 +589,10 @@ func DialRanker(d network.DialRanker) Option { } } -// UDPBlackHoleConfig configures libp2p to use c as the config for UDP black hole detection -func UDPBlackHoleConfig(c *swarm.BlackHoleConfig) Option { +// SwarmOpts configures libp2p to use swarm with opts +func SwarmOpts(opts ...swarm.Option) Option { return func(cfg *Config) error { - if c == nil { - return errors.New("udp black hole config cannot be nil") - } - cfg.UDPBlackHoleConfig = c - return nil - } -} - -// IPv6BlackHoleConfig configures libp2p to use c as the config for IPv6 black hole detection -func IPv6BlackHoleConfig(c *swarm.BlackHoleConfig) Option { - return func(cfg *Config) error { - if c == nil { - return errors.New("ipv6 black hole config cannot be nil") - } - cfg.IPv6BlackHoleConfig = c + cfg.SwarmOpts = opts return nil } } diff --git a/p2p/net/swarm/black_hole_detector.go b/p2p/net/swarm/black_hole_detector.go index 990b17d7bd..078b1126c4 100644 --- a/p2p/net/swarm/black_hole_detector.go +++ b/p2p/net/swarm/black_hole_detector.go @@ -241,8 +241,8 @@ func (d *blackHoleDetector) RecordResult(addr ma.Multiaddr, success bool) { } } -// BlackHoleConfig is the config used for black hole detection -type BlackHoleConfig struct { +// 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 @@ -252,10 +252,10 @@ type BlackHoleConfig struct { MinSuccesses int } -func newBlackHoleDetector(udpConfig, ipv6Config *BlackHoleConfig, mt MetricsTracer) *blackHoleDetector { +func newBlackHoleDetector(udpConfig, ipv6Config blackHoleConfig, mt MetricsTracer) *blackHoleDetector { d := &blackHoleDetector{} - if udpConfig != nil && udpConfig.Enabled { + if udpConfig.Enabled { d.udp = &blackHoleFilter{ n: udpConfig.N, minSuccesses: udpConfig.MinSuccesses, @@ -264,7 +264,7 @@ func newBlackHoleDetector(udpConfig, ipv6Config *BlackHoleConfig, mt MetricsTrac } } - if ipv6Config != nil && ipv6Config.Enabled { + if ipv6Config.Enabled { d.ipv6 = &blackHoleFilter{ n: ipv6Config.N, minSuccesses: ipv6Config.MinSuccesses, diff --git a/p2p/net/swarm/black_hole_detector_test.go b/p2p/net/swarm/black_hole_detector_test.go index bb8fc8699b..7b10fc88a6 100644 --- a/p2p/net/swarm/black_hole_detector_test.go +++ b/p2p/net/swarm/black_hole_detector_test.go @@ -75,8 +75,8 @@ func TestBlackHoleFilterSuccessFraction(t *testing.T) { } func TestBlackHoleDetectorInApplicableAddress(t *testing.T) { - udpConfig := &BlackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} - ipv6Config := &BlackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} + 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"), @@ -94,8 +94,8 @@ func TestBlackHoleDetectorInApplicableAddress(t *testing.T) { } func TestBlackHoleDetectorUDPDisabled(t *testing.T) { - ipv6Config := &BlackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} - bhd := newBlackHoleDetector(nil, ipv6Config, 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++ { @@ -106,8 +106,8 @@ func TestBlackHoleDetectorUDPDisabled(t *testing.T) { } func TestBlackHoleDetectorIPv6Disabled(t *testing.T) { - udpConfig := &BlackHoleConfig{Enabled: true, N: 10, MinSuccesses: 5} - bhd := newBlackHoleDetector(udpConfig, nil, 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 0032386aae..9dfab2d9c0 100644 --- a/p2p/net/swarm/swarm.go +++ b/p2p/net/swarm/swarm.go @@ -109,23 +109,21 @@ func WithDialRanker(d network.DialRanker) Option { } // WithUDPBlackHoleConfig configures swarm to use c as the config for UDP black hole detection -func WithUDPBlackHoleConfig(c *BlackHoleConfig) Option { +// 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 { - if c == nil { - return errors.New("udp black hole config cannot be nil") - } - s.udpBlackHoleConfig = c + 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 -func WithIPv6BlackHoleConfig(c *BlackHoleConfig) Option { +// 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 { - if c == nil { - return errors.New("ipv6 black hole config cannot be nil") - } - s.ipv6BlackHoleConfig = c + s.ipv6BlackHoleConfig = blackHoleConfig{Enabled: enabled, N: n, MinSuccesses: min} return nil } } @@ -196,8 +194,8 @@ type Swarm struct { dialRanker network.DialRanker - udpBlackHoleConfig *BlackHoleConfig - ipv6BlackHoleConfig *BlackHoleConfig + udpBlackHoleConfig blackHoleConfig + ipv6BlackHoleConfig blackHoleConfig bhd *blackHoleDetector } @@ -222,8 +220,8 @@ func NewSwarm(local peer.ID, peers peerstore.Peerstore, eventBus event.Bus, opts // 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: 10, MinSuccesses: 5}, + udpBlackHoleConfig: blackHoleConfig{Enabled: true, N: 100, MinSuccesses: 5}, + ipv6BlackHoleConfig: blackHoleConfig{Enabled: true, N: 100, MinSuccesses: 5}, } s.conns.m = make(map[peer.ID][]*Conn)