diff --git a/p2p/host/autonat/autonat.go b/p2p/host/autonat/autonat.go index 7e839dddd2..2d6c9786f9 100644 --- a/p2p/host/autonat/autonat.go +++ b/p2p/host/autonat/autonat.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/libp2p/go-libp2p-core/event" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" @@ -61,6 +62,10 @@ type AmbientAutoNAT struct { // If only a single autoNAT peer is known, then the confidence increases // for each failure until it reaches 3. confidence int + + emitUnknown event.Emitter + emitPublic event.Emitter + emitPrivate event.Emitter } // NewAutoNAT creates a new ambient NAT autodiscovery instance attached to a host @@ -70,12 +75,20 @@ func NewAutoNAT(ctx context.Context, h host.Host, getAddrs GetAddrs) AutoNAT { getAddrs = h.Addrs } + emitUnknown, _ := h.EventBus().Emitter(new(event.EvtLocalRoutabilityUnknown)) + emitPublic, _ := h.EventBus().Emitter(new(event.EvtLocalRoutabilityPublic)) + emitPrivate, _ := h.EventBus().Emitter(new(event.EvtLocalRoutabilityPrivate)) + as := &AmbientAutoNAT{ ctx: ctx, host: h, getAddrs: getAddrs, peers: make(map[peer.ID][]ma.Multiaddr), status: NATStatusUnknown, + + emitUnknown: emitUnknown, + emitPublic: emitPublic, + emitPrivate: emitPrivate, } h.Network().Notify(as) @@ -90,6 +103,18 @@ func (as *AmbientAutoNAT) Status() NATStatus { return as.status } +func (as *AmbientAutoNAT) updateStatus(s NATStatus) { + as.status = s + switch s { + case NATStatusUnknown: + as.emitUnknown.Emit(event.EvtLocalRoutabilityUnknown{}) + case NATStatusPublic: + as.emitPublic.Emit(event.EvtLocalRoutabilityPublic{}) + case NATStatusPrivate: + as.emitPrivate.Emit(event.EvtLocalRoutabilityPrivate{}) + } +} + func (as *AmbientAutoNAT) PublicAddr() (ma.Multiaddr, error) { as.mx.Lock() defer as.mx.Unlock() @@ -194,8 +219,8 @@ func (as *AmbientAutoNAT) autodetect() { } else if as.confidence < 3 { as.confidence++ } - as.status = NATStatusPublic as.addr = result.pubaddr + as.updateStatus(NATStatusPublic) } else if result.private > 0 { log.Debugf("NAT status is private") if as.status == NATStatusPublic { @@ -204,15 +229,15 @@ func (as *AmbientAutoNAT) autodetect() { } else if as.confidence < 3 { as.confidence++ } - as.status = NATStatusPrivate as.addr = nil + as.updateStatus(NATStatusPrivate) } else if as.confidence > 0 { // don't just flip to unknown, reduce confidence first as.confidence-- } else { log.Debugf("NAT status is unknown") - as.status = NATStatusUnknown as.addr = nil + as.updateStatus(NATStatusUnknown) } as.mx.Unlock() } diff --git a/p2p/host/autonat/autonat_test.go b/p2p/host/autonat/autonat_test.go index e5fc7fff37..74a429ca4f 100644 --- a/p2p/host/autonat/autonat_test.go +++ b/p2p/host/autonat/autonat_test.go @@ -6,10 +6,10 @@ import ( "time" pb "github.com/libp2p/go-libp2p-autonat/pb" - "github.com/libp2p/go-libp2p-core/peer" - + "github.com/libp2p/go-libp2p-core/event" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p-core/peer" ggio "github.com/gogo/protobuf/io" bhost "github.com/libp2p/go-libp2p-blankhost" @@ -96,6 +96,12 @@ func TestAutoNATPrivate(t *testing.T) { hs := makeAutoNATServicePrivate(ctx, t) hc, an := makeAutoNAT(ctx, t, hs) + // subscribe to AutoNat events + s, err := hc.EventBus().Subscribe(&event.EvtLocalRoutabilityPrivate{}) + if err != nil { + t.Fatalf("failed to subscribe to event EvtLocalRoutabilityPrivate, err=%s", err) + } + status := an.Status() if status != NATStatusUnknown { t.Fatalf("unexpected NAT status: %d", status) @@ -108,6 +114,17 @@ func TestAutoNATPrivate(t *testing.T) { if status != NATStatusPrivate { t.Fatalf("unexpected NAT status: %d", status) } + + select { + case e := <-s.Out(): + _, ok := e.(event.EvtLocalRoutabilityPrivate) + if !ok { + t.Fatal("got wrong event type from the bus") + } + + case <-time.After(1 * time.Second): + t.Fatal("failed to get the EvtLocalRoutabilityPrivate event from the bus") + } } func TestAutoNATPublic(t *testing.T) { @@ -117,6 +134,12 @@ func TestAutoNATPublic(t *testing.T) { hs := makeAutoNATServicePublic(ctx, t) hc, an := makeAutoNAT(ctx, t, hs) + // subscribe to AutoNat events + s, err := hc.EventBus().Subscribe(&event.EvtLocalRoutabilityPublic{}) + if err != nil { + t.Fatalf("failed to subscribe to event EvtLocalRoutabilityPublic, err=%s", err) + } + status := an.Status() if status != NATStatusUnknown { t.Fatalf("unexpected NAT status: %d", status) @@ -129,4 +152,15 @@ func TestAutoNATPublic(t *testing.T) { if status != NATStatusPublic { t.Fatalf("unexpected NAT status: %d", status) } + + select { + case e := <-s.Out(): + _, ok := e.(event.EvtLocalRoutabilityPublic) + if !ok { + t.Fatal("got wrong event type from the bus") + } + + case <-time.After(1 * time.Second): + t.Fatal("failed to get the EvtLocalRoutabilityPublic event from the bus") + } }