Skip to content
This repository has been archived by the owner on May 11, 2022. It is now read-only.

Commit

Permalink
Merge pull request #20 from libp2p/feat/parallel-dialback
Browse files Browse the repository at this point in the history
parallelize dialbacks
  • Loading branch information
vyzo committed Apr 4, 2019
2 parents 36aba7c + 2584eaf commit c6b40f9
Showing 1 changed file with 71 additions and 37 deletions.
108 changes: 71 additions & 37 deletions autonat.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var (
AutoNATBootDelay = 15 * time.Second
AutoNATRetryInterval = 90 * time.Second
AutoNATRefreshInterval = 15 * time.Minute
AutoNATRequestTimeout = 60 * time.Second
AutoNATRequestTimeout = 30 * time.Second
)

// AutoNAT is the interface for ambient NAT autodiscovery
Expand Down Expand Up @@ -135,50 +135,84 @@ func (as *AmbientAutoNAT) autodetect() {
}

cli := NewAutoNATClient(as.host, as.getAddrs)
failures := 0

for _, pi := range peers {
ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout)
as.host.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)
a, err := cli.DialBack(ctx, pi.ID)
cancel()

switch {
case err == nil:
log.Debugf("NAT status is public; address through %s: %s", pi.ID.Pretty(), a.String())
as.mx.Lock()
as.addr = a
as.status = NATStatusPublic
as.confidence = 0
as.mx.Unlock()
return
ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout)
defer cancel()

var result struct {
sync.Mutex
private int
public int
pubaddr ma.Multiaddr
}

case IsDialError(err):
log.Debugf("dial error through %s: %s", pi.ID.Pretty(), err.Error())
failures++
if failures >= 3 || as.confidence >= 3 { // 3 times is enemy action
log.Debugf("NAT status is private")
as.mx.Lock()
as.status = NATStatusPrivate
as.confidence = 3
as.mx.Unlock()
return
}
probe := 3 - as.confidence
if probe == 0 {
probe = 1
}
if probe > len(peers) {
probe = len(peers)
}

default:
log.Debugf("Error dialing through %s: %s", pi.ID.Pretty(), err.Error())
}
var wg sync.WaitGroup

for _, pi := range peers[:probe] {
wg.Add(1)
go func(pi pstore.PeerInfo) {
defer wg.Done()

as.host.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)
a, err := cli.DialBack(ctx, pi.ID)

switch {
case err == nil:
log.Debugf("Dialback through %s successful; public address is %s", pi.ID.Pretty(), a.String())
result.Lock()
result.public++
result.pubaddr = a
result.Unlock()

case IsDialError(err):
log.Debugf("Dialback through %s failed", pi.ID.Pretty())
result.Lock()
result.private++
result.Unlock()

default:
log.Debugf("Dialback error through %s: %s", pi.ID.Pretty(), err)
}
}(pi)
}

wg.Wait()

as.mx.Lock()
if failures > 0 {
as.status = NATStatusPrivate
as.confidence++
if result.public > 0 {
log.Debugf("NAT status is public")
if as.status == NATStatusPrivate {
// we are flipping our NATStatus, so confidence drops to 0
as.confidence = 0
} else if as.confidence < 3 {
as.confidence++
}
as.status = NATStatusPublic
as.addr = result.pubaddr
} else if result.private > 0 {
log.Debugf("NAT status is private")
if as.status == NATStatusPublic {
// we are flipping our NATStatus, so confidence drops to 0
as.confidence = 0
} else if as.confidence < 3 {
as.confidence++
}
as.status = NATStatusPrivate
as.addr = nil
} else if as.confidence > 0 {
// don't just flip to unknown, reduce confidence first
as.confidence--
} else {
as.status = NATStatusUnknown
as.confidence = 0
log.Debugf("NAT status is unknown")
as.status = NATStatusUnknown
as.addr = nil
}
as.mx.Unlock()
}
Expand Down

0 comments on commit c6b40f9

Please sign in to comment.