Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

autonatv2: implement autonatv2 spec #2469

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
98 changes: 94 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/libp2p/go-libp2p/p2p/host/autonat"
"github.com/libp2p/go-libp2p/p2p/host/autorelay"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
blankhost "github.com/libp2p/go-libp2p/p2p/host/blank"
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
"github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem"
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
Expand Down Expand Up @@ -131,6 +132,13 @@ type Config struct {
SwarmOpts []swarm.Option

DisableIdentifyAddressDiscovery bool

DisableAutoNATv2 bool

UDPBlackHoleFilter *swarm.BlackHoleFilter
CustomUDPBlackHoleFilter bool
IPv6BlackHoleFilter *swarm.BlackHoleFilter
CustomIPv6BlackHoleFilter bool
}

func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) {
Expand Down Expand Up @@ -165,7 +173,10 @@ func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swa
return nil, err
}

opts := cfg.SwarmOpts
opts := append(cfg.SwarmOpts,
swarm.WithUDPBlackHoleFilter(cfg.UDPBlackHoleFilter),
swarm.WithIPv6BlackHoleFilter(cfg.IPv6BlackHoleFilter),
)
if cfg.Reporter != nil {
opts = append(opts, swarm.WithMetrics(cfg.Reporter))
}
Expand Down Expand Up @@ -193,6 +204,76 @@ func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swa
return swarm.NewSwarm(pid, cfg.Peerstore, eventBus, opts...)
}

func (cfg *Config) makeAutoNATV2Host() (host.Host, error) {
autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
return nil, err
}
ps, err := pstoremem.NewPeerstore()
if err != nil {
return nil, err
}

autoNatCfg := Config{
Transports: cfg.Transports,
Muxers: cfg.Muxers,
SecurityTransports: cfg.SecurityTransports,
Insecure: cfg.Insecure,
PSK: cfg.PSK,
ConnectionGater: cfg.ConnectionGater,
Reporter: cfg.Reporter,
PeerKey: autonatPrivKey,
Peerstore: ps,
DialRanker: swarm.NoDelayDialRanker,
UDPBlackHoleFilter: cfg.UDPBlackHoleFilter,
IPv6BlackHoleFilter: cfg.IPv6BlackHoleFilter,
SwarmOpts: []swarm.Option{
// Don't update black hole state for failed autonat dials
swarm.WithReadOnlyBlackHoleDetector(),
},
}
fxopts, err := autoNatCfg.addTransports()
if err != nil {
return nil, err
}
var dialerHost host.Host
fxopts = append(fxopts,
fx.Provide(eventbus.NewBus),
fx.Provide(func(lifecycle fx.Lifecycle, b event.Bus) (*swarm.Swarm, error) {
lifecycle.Append(fx.Hook{
OnStop: func(context.Context) error {
return ps.Close()
}})
sw, err := autoNatCfg.makeSwarm(b, false)
return sw, err
}),
fx.Provide(func(sw *swarm.Swarm) *blankhost.BlankHost {
return blankhost.NewBlankHost(sw)
}),
fx.Provide(func(bh *blankhost.BlankHost) host.Host {
return bh
}),
fx.Provide(func() crypto.PrivKey { return autonatPrivKey }),
fx.Provide(func(bh host.Host) peer.ID { return bh.ID() }),
fx.Invoke(func(bh *blankhost.BlankHost) {
dialerHost = bh
}),
)
app := fx.New(fxopts...)
if err := app.Err(); err != nil {
return nil, err
}
err = app.Start(context.Background())
if err != nil {
return nil, err
}
go func() {
<-dialerHost.Network().(*swarm.Swarm).Done()
app.Stop(context.Background())
}()
return dialerHost, nil
}

func (cfg *Config) addTransports() ([]fx.Option, error) {
fxopts := []fx.Option{
fx.WithLogger(func() fxevent.Logger { return getFXLogger() }),
Expand Down Expand Up @@ -291,6 +372,14 @@ func (cfg *Config) addTransports() ([]fx.Option, error) {
}

func (cfg *Config) newBasicHost(swrm *swarm.Swarm, eventBus event.Bus) (*bhost.BasicHost, error) {
var autonatv2Dialer host.Host
if !cfg.DisableAutoNATv2 {
ah, err := cfg.makeAutoNATV2Host()
if err != nil {
return nil, err
}
autonatv2Dialer = ah
}
h, err := bhost.NewHost(swrm, &bhost.HostOpts{
EventBus: eventBus,
ConnManager: cfg.ConnManager,
Expand All @@ -306,6 +395,8 @@ func (cfg *Config) newBasicHost(swrm *swarm.Swarm, eventBus event.Bus) (*bhost.B
EnableMetrics: !cfg.DisableMetrics,
PrometheusRegisterer: cfg.PrometheusRegisterer,
DisableIdentifyAddressDiscovery: cfg.DisableIdentifyAddressDiscovery,
EnableAutoNATv2: !cfg.DisableAutoNATv2,
AutoNATv2Dialer: autonatv2Dialer,
})
if err != nil {
return nil, err
Expand Down Expand Up @@ -481,9 +572,8 @@ func (cfg *Config) addAutoNAT(h *bhost.BasicHost) error {
Peerstore: ps,
DialRanker: swarm.NoDelayDialRanker,
SwarmOpts: []swarm.Option{
// It is better to disable black hole detection and just attempt a dial for autonat
swarm.WithUDPBlackHoleConfig(false, 0, 0),
swarm.WithIPv6BlackHoleConfig(false, 0, 0),
swarm.WithUDPBlackHoleFilter(nil),
swarm.WithIPv6BlackHoleFilter(nil),
},
}

Expand Down
3 changes: 3 additions & 0 deletions core/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ type Dialer interface {
// Notify/StopNotify register and unregister a notifiee for signals
Notify(Notifiee)
StopNotify(Notifiee)

// CanDial returns whether the dialer can dial peer p at addr
CanDial(p peer.ID, addr ma.Multiaddr) bool
}

// AddrDelay provides an address along with the delay after which the address
Expand Down
25 changes: 25 additions & 0 deletions defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
"github.com/libp2p/go-libp2p/p2p/muxer/yamux"
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
"github.com/libp2p/go-libp2p/p2p/net/swarm"
"github.com/libp2p/go-libp2p/p2p/security/noise"
tls "github.com/libp2p/go-libp2p/p2p/security/tls"
quic "github.com/libp2p/go-libp2p/p2p/transport/quic"
Expand Down Expand Up @@ -133,6 +134,18 @@ var DefaultPrometheusRegisterer = func(cfg *Config) error {
return cfg.Apply(PrometheusRegisterer(prometheus.DefaultRegisterer))
}

var defaultUDPBlackHoleDetector = func(cfg *Config) error {
// A black hole is a binary property. On a network if UDP dials are blocked, all dials will
// fail. So a low success rate of 5 out 100 dials is good enough.
return cfg.Apply(UDPBlackHoleFilter(&swarm.BlackHoleFilter{N: 100, MinSuccesses: 5, Name: "UDP"}))
}

var defaultIPv6BlackHoleDetector = func(cfg *Config) error {
// A black hole is a binary property. On a network if there is no IPv6 connectivity, all
// dials will fail. So a low success rate of 5 out 100 dials is good enough.
return cfg.Apply(IPv6BlackHoleFilter(&swarm.BlackHoleFilter{N: 100, MinSuccesses: 5, Name: "IPv6"}))
}

// Complete list of default options and when to fallback on them.
//
// Please *DON'T* specify default options any other way. Putting this all here
Expand Down Expand Up @@ -189,6 +202,18 @@ var defaults = []struct {
fallback: func(cfg *Config) bool { return !cfg.DisableMetrics && cfg.PrometheusRegisterer == nil },
opt: DefaultPrometheusRegisterer,
},
{
fallback: func(cfg *Config) bool {
return !cfg.CustomUDPBlackHoleFilter && cfg.UDPBlackHoleFilter == nil
},
opt: defaultUDPBlackHoleDetector,
},
{
fallback: func(cfg *Config) bool {
return !cfg.CustomIPv6BlackHoleFilter && cfg.IPv6BlackHoleFilter == nil
},
opt: defaultIPv6BlackHoleDetector,
},
}

// Defaults configures libp2p to use the default options. Can be combined with
Expand Down
26 changes: 26 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,29 @@ func DisableIdentifyAddressDiscovery() Option {
return nil
}
}

// DisableAutoNATv2 disables autonat
func DisableAutoNATv2() Option {
return func(cfg *Config) error {
cfg.DisableAutoNATv2 = true
return nil
}
}

// UDPBlackHoleFilter configures libp2p to use f as the black hole filter for UDP addrs
func UDPBlackHoleFilter(f *swarm.BlackHoleFilter) Option {
return func(cfg *Config) error {
cfg.UDPBlackHoleFilter = f
cfg.CustomUDPBlackHoleFilter = true
return nil
}
}

// IPv6BlackHoleFilter configures libp2p to use f as the black hole filter for IPv6 addrs
func IPv6BlackHoleFilter(f *swarm.BlackHoleFilter) Option {
return func(cfg *Config) error {
cfg.IPv6BlackHoleFilter = f
cfg.CustomIPv6BlackHoleFilter = true
return nil
}
}
21 changes: 21 additions & 0 deletions p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
"github.com/libp2p/go-libp2p/p2p/host/pstoremanager"
"github.com/libp2p/go-libp2p/p2p/host/relaysvc"
"github.com/libp2p/go-libp2p/p2p/protocol/autonatv2"
relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
Expand Down Expand Up @@ -105,6 +106,8 @@ type BasicHost struct {
caBook peerstore.CertifiedAddrBook

autoNat autonat.AutoNAT

autonatv2 *autonatv2.AutoNAT
}

var _ host.Host = (*BasicHost)(nil)
Expand Down Expand Up @@ -167,6 +170,8 @@ type HostOpts struct {

// DisableIdentifyAddressDiscovery disables address discovery using peer provided observed addresses in identify
DisableIdentifyAddressDiscovery bool
EnableAutoNATv2 bool
AutoNATv2Dialer host.Host
}

// NewHost constructs a new *BasicHost and activates it by attaching its stream and connection handlers to the given inet.Network.
Expand Down Expand Up @@ -310,6 +315,13 @@ func NewHost(n network.Network, opts *HostOpts) (*BasicHost, error) {
h.pings = ping.NewPingService(h)
}

if opts.EnableAutoNATv2 {
h.autonatv2, err = autonatv2.New(h, opts.AutoNATv2Dialer)
if err != nil {
return nil, fmt.Errorf("failed to create autonatv2: %w", err)
}
}

n.SetStreamHandler(h.newStreamHandler)

// register to be notified when the network's listen addrs change,
Expand Down Expand Up @@ -398,6 +410,12 @@ func (h *BasicHost) Start() {
h.psManager.Start()
h.refCount.Add(1)
h.ids.Start()
if h.autonatv2 != nil {
err := h.autonatv2.Start()
if err != nil {
log.Errorf("autonat v2 failed to start: %s", err)
}
}
go h.background()
}

Expand Down Expand Up @@ -1100,6 +1118,9 @@ func (h *BasicHost) Close() error {
if h.hps != nil {
h.hps.Close()
}
if h.autonatv2 != nil {
h.autonatv2.Close()
}

_ = h.emitters.evtLocalProtocolsUpdated.Close()
_ = h.emitters.evtLocalAddrsUpdated.Close()
Expand Down
4 changes: 4 additions & 0 deletions p2p/net/mock/mock_peernet.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,7 @@ func (pn *peernet) notifyAll(notification func(f network.Notifiee)) {
func (pn *peernet) ResourceManager() network.ResourceManager {
return &network.NullResourceManager{}
}

func (pn *peernet) CanDial(p peer.ID, addr ma.Multiaddr) bool {
return true
}