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

move go-libp2p-nat here #1513

Merged
merged 46 commits into from
May 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
843a30c
move to p2p dir
jbenet Sep 30, 2015
0be91aa
extract from 0.4.0
whyrusleeping Nov 12, 2015
fa375b9
more vendoring
whyrusleeping Nov 15, 2015
aa5c8a2
fixes for sha3
whyrusleeping Nov 16, 2015
2c7dfee
vendor in notifier
whyrusleeping Nov 16, 2015
ac1daff
remove multiple multihash deps
whyrusleeping Nov 16, 2015
f2b4ef2
migrate to gx namespace
whyrusleeping Nov 18, 2015
8863821
WIP
whyrusleeping Dec 7, 2015
eff52d2
path rewrites
whyrusleeping Jan 4, 2016
122bc10
update version of go-multiaddr
whyrusleeping Mar 3, 2016
a61a7ad
update utp lib
whyrusleeping Mar 30, 2016
b6ac01b
recursive dependency update of utp lib
whyrusleeping Apr 27, 2016
7c74ee6
rewrite all package paths to dvcs
whyrusleeping Apr 27, 2016
4780826
fix minor race condition in nat detection code
whyrusleeping Jun 15, 2016
977485d
gosimple
John-Steidley Jul 23, 2016
3339abd
Retry NAT punching without duration on mapping failure
Kubuxu Aug 26, 2016
59a1cee
nat: do not shortcircuit permanent mappings
Kubuxu Aug 26, 2016
72d32fc
nat: split into files
Kubuxu Sep 12, 2016
d44566d
nat: add locks around nat
Kubuxu Sep 12, 2016
c803102
fix locking issue introduced in 90eeff42fcd
Kubuxu Sep 29, 2016
e7fa567
update deps
whyrusleeping Oct 5, 2016
0ee4048
Fix "go vet" errors.
kevina Nov 17, 2017
0ec0019
only map *usable* addresses
Stebalien Aug 16, 2018
3011636
Merge pull request #7 from libp2p/fix/map-usable
Stebalien Sep 1, 2018
d6a73bd
remove all uses of multiaddrs
Stebalien Mar 5, 2019
77f74e3
Merge pull request #14 from libp2p/feat/no-multiddr
Stebalien Mar 6, 2019
8d26ea3
switch to libp2p's go-nat fork
Stebalien Mar 12, 2019
d065e35
Merge pull request #16 from libp2p/feat/use-libp2p-go-nat
Stebalien Mar 12, 2019
0c48498
fix: remove notifier
Stebalien May 2, 2019
989b3bb
Merge pull request #18 from libp2p/fix/remove-notifier
raulk May 2, 2019
9029627
nit: fix log format
Stebalien Jul 31, 2019
cdec75a
Merge pull request #19 from libp2p/fix/log-format
Stebalien Jul 31, 2019
33f0983
Update nat.go
arberiii Jan 12, 2020
9254b00
Merge pull request #26 from arberiii/master
Stebalien Jan 13, 2020
8aecd4d
remove unused field permanent from mapping
marten-seemann May 5, 2021
15ffec9
Merge pull request #33 from libp2p/fix-staticcheck
marten-seemann May 5, 2021
97b8eb5
chore: update go-log
Stebalien Sep 15, 2021
215ac91
Merge pull request #37 from libp2p/chore/update-log
Stebalien Sep 15, 2021
67d483b
update go-nat to v0.1.0
marten-seemann Sep 19, 2021
1ce83db
stop using goprocess for shutdown
marten-seemann Sep 19, 2021
5e8e3d8
remove mapping.teardown
marten-seemann Sep 25, 2021
bbf8637
Merge pull request #38 from libp2p/remove-goprocess
marten-seemann Sep 25, 2021
7201aee
Fix error log
arajasek Oct 1, 2021
1e31180
Merge pull request #40 from libp2p/asr/fix-error
Stebalien Oct 5, 2021
1887bf4
move go-libp2p-nat here
marten-seemann May 20, 2022
f656048
switch from github.com/libp2p/go-libp2p-nat to p2p/net/nat
marten-seemann May 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ require (
github.com/libp2p/go-libp2p-asn-util v0.1.0
github.com/libp2p/go-libp2p-circuit v0.6.0
github.com/libp2p/go-libp2p-core v0.15.1
github.com/libp2p/go-libp2p-nat v0.1.0
github.com/libp2p/go-libp2p-peerstore v0.6.0
github.com/libp2p/go-libp2p-resource-manager v0.2.1
github.com/libp2p/go-libp2p-testing v0.9.2
github.com/libp2p/go-mplex v0.7.0
github.com/libp2p/go-msgio v0.2.0
github.com/libp2p/go-nat v0.1.0
github.com/libp2p/go-netroute v0.2.0
github.com/libp2p/go-reuseport v0.1.0
github.com/libp2p/go-reuseport-transport v0.1.0
Expand Down Expand Up @@ -84,7 +84,6 @@ require (
github.com/libp2p/go-libp2p-tls v0.4.1 // indirect
github.com/libp2p/go-libp2p-transport-upgrader v0.7.1 // indirect
github.com/libp2p/go-libp2p-yamux v0.9.1 // indirect
github.com/libp2p/go-nat v0.1.0 // indirect
github.com/libp2p/go-openssl v0.0.7 // indirect
github.com/libp2p/go-tcp-transport v0.5.1 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,6 @@ github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9
github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs=
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
Expand Down Expand Up @@ -442,8 +441,6 @@ github.com/libp2p/go-libp2p-core v0.15.1 h1:0RY+Mi/ARK9DgG1g9xVQLb8dDaaU8tCePMtG
github.com/libp2p/go-libp2p-core v0.15.1/go.mod h1:agSaboYM4hzB1cWekgVReqV5M4g5M+2eNNejV+1EEhs=
github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g=
github.com/libp2p/go-libp2p-mplex v0.5.0/go.mod h1:eLImPJLkj3iG5t5lq68w3Vm5NAQ5BcKwrrb2VmOYb3M=
github.com/libp2p/go-libp2p-nat v0.1.0 h1:vigUi2MEN+fwghe5ijpScxtbbDz+L/6y8XwlzYOJgSY=
github.com/libp2p/go-libp2p-nat v0.1.0/go.mod h1:DQzAG+QbDYjN1/C3B6vXucLtz3u9rEonLVPtZVzQqks=
github.com/libp2p/go-libp2p-peerstore v0.4.0/go.mod h1:rDJUFyzEWPpXpEwywkcTYYzDHlwza8riYMaUzaN6hX0=
github.com/libp2p/go-libp2p-peerstore v0.6.0 h1:HJminhQSGISBIRb93N6WK3t6Fa8OOTnHd/VBjL4mY5A=
github.com/libp2p/go-libp2p-peerstore v0.6.0/go.mod h1:DGEmKdXrcYpK9Jha3sS7MhqYdInxJy84bIPtSu65bKc=
Expand Down Expand Up @@ -852,7 +849,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
Expand Down
2 changes: 1 addition & 1 deletion p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/libp2p/go-libp2p/p2p/host/autonat"
"github.com/libp2p/go-libp2p/p2p/host/pstoremanager"
"github.com/libp2p/go-libp2p/p2p/host/relaysvc"
inat "github.com/libp2p/go-libp2p/p2p/net/nat"
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 All @@ -28,7 +29,6 @@ import (
"github.com/libp2p/go-libp2p-core/record"

"github.com/libp2p/go-eventbus"
inat "github.com/libp2p/go-libp2p-nat"
"github.com/libp2p/go-netroute"

logging "github.com/ipfs/go-log/v2"
Expand Down
4 changes: 3 additions & 1 deletion p2p/host/basic/natmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"sync"
"time"

inat "github.com/libp2p/go-libp2p/p2p/net/nat"

"github.com/libp2p/go-libp2p-core/network"
inat "github.com/libp2p/go-libp2p-nat"

ma "github.com/multiformats/go-multiaddr"
)

Expand Down
119 changes: 119 additions & 0 deletions p2p/net/nat/mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package nat

import (
"fmt"
"net"
"sync"
"time"
)

// Mapping represents a port mapping in a NAT.
type Mapping interface {
// NAT returns the NAT object this Mapping belongs to.
NAT() *NAT

// Protocol returns the protocol of this port mapping. This is either
// "tcp" or "udp" as no other protocols are likely to be NAT-supported.
Protocol() string

// InternalPort returns the internal device port. Mapping will continue to
// try to map InternalPort() to an external facing port.
InternalPort() int

// ExternalPort returns the external facing port. If the mapping is not
// established, port will be 0
ExternalPort() int

// ExternalAddr returns the external facing address. If the mapping is not
// established, addr will be nil, and and ErrNoMapping will be returned.
ExternalAddr() (addr net.Addr, err error)

// Close closes the port mapping
Close() error
}

// keeps republishing
type mapping struct {
sync.Mutex // guards all fields

nat *NAT
proto string
intport int
extport int

cached net.IP
cacheTime time.Time
cacheLk sync.Mutex
}

func (m *mapping) NAT() *NAT {
m.Lock()
defer m.Unlock()
return m.nat
}

func (m *mapping) Protocol() string {
m.Lock()
defer m.Unlock()
return m.proto
}

func (m *mapping) InternalPort() int {
m.Lock()
defer m.Unlock()
return m.intport
}

func (m *mapping) ExternalPort() int {
m.Lock()
defer m.Unlock()
return m.extport
}

func (m *mapping) setExternalPort(p int) {
m.Lock()
defer m.Unlock()
m.extport = p
}

func (m *mapping) ExternalAddr() (net.Addr, error) {
m.cacheLk.Lock()
defer m.cacheLk.Unlock()
oport := m.ExternalPort()
if oport == 0 {
// dont even try right now.
return nil, ErrNoMapping
}

if time.Since(m.cacheTime) >= CacheTime {
m.nat.natmu.Lock()
cval, err := m.nat.nat.GetExternalAddress()
m.nat.natmu.Unlock()

if err != nil {
return nil, err
}

m.cached = cval
m.cacheTime = time.Now()
}
switch m.Protocol() {
case "tcp":
return &net.TCPAddr{
IP: m.cached,
Port: oport,
}, nil
case "udp":
return &net.UDPAddr{
IP: m.cached,
Port: oport,
}, nil
default:
panic(fmt.Sprintf("invalid protocol %q", m.Protocol()))
}
}

func (m *mapping) Close() error {
m.nat.removeMapping(m)
return nil
}
193 changes: 193 additions & 0 deletions p2p/net/nat/nat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package nat

import (
"context"
"errors"
"fmt"
"sync"
"time"

logging "github.com/ipfs/go-log/v2"

"github.com/libp2p/go-nat"
)

// ErrNoMapping signals no mapping exists for an address
var ErrNoMapping = errors.New("mapping not established")

var log = logging.Logger("nat")

// MappingDuration is a default port mapping duration.
// Port mappings are renewed every (MappingDuration / 3)
const MappingDuration = time.Second * 60

// CacheTime is the time a mapping will cache an external address for
const CacheTime = time.Second * 15

// DiscoverNAT looks for a NAT device in the network and
// returns an object that can manage port mappings.
func DiscoverNAT(ctx context.Context) (*NAT, error) {
natInstance, err := nat.DiscoverGateway(ctx)
if err != nil {
return nil, err
}

// Log the device addr.
addr, err := natInstance.GetDeviceAddress()
if err != nil {
log.Debug("DiscoverGateway address error:", err)
} else {
log.Debug("DiscoverGateway address:", addr)
}

return newNAT(natInstance), nil
}

// NAT is an object that manages address port mappings in
// NATs (Network Address Translators). It is a long-running
// service that will periodically renew port mappings,
// and keep an up-to-date list of all the external addresses.
type NAT struct {
natmu sync.Mutex
nat nat.NAT

refCount sync.WaitGroup
ctx context.Context
ctxCancel context.CancelFunc

mappingmu sync.RWMutex // guards mappings
closed bool
mappings map[*mapping]struct{}
}

func newNAT(realNAT nat.NAT) *NAT {
ctx, cancel := context.WithCancel(context.Background())
return &NAT{
nat: realNAT,
mappings: make(map[*mapping]struct{}),
ctx: ctx,
ctxCancel: cancel,
}
}

// Close shuts down all port mappings. NAT can no longer be used.
func (nat *NAT) Close() error {
nat.mappingmu.Lock()
nat.closed = true
nat.mappingmu.Unlock()

nat.ctxCancel()
nat.refCount.Wait()
return nil
}

// Mappings returns a slice of all NAT mappings
func (nat *NAT) Mappings() []Mapping {
nat.mappingmu.Lock()
maps2 := make([]Mapping, 0, len(nat.mappings))
for m := range nat.mappings {
maps2 = append(maps2, m)
}
nat.mappingmu.Unlock()
return maps2
}

// NewMapping attempts to construct a mapping on protocol and internal port
// It will also periodically renew the mapping until the returned Mapping
// -- or its parent NAT -- is Closed.
//
// May not succeed, and mappings may change over time;
// NAT devices may not respect our port requests, and even lie.
// Clients should not store the mapped results, but rather always
// poll our object for the latest mappings.
func (nat *NAT) NewMapping(protocol string, port int) (Mapping, error) {
if nat == nil {
return nil, fmt.Errorf("no nat available")
}

switch protocol {
case "tcp", "udp":
default:
return nil, fmt.Errorf("invalid protocol: %s", protocol)
}

m := &mapping{
intport: port,
nat: nat,
proto: protocol,
}

nat.mappingmu.Lock()
if nat.closed {
nat.mappingmu.Unlock()
return nil, errors.New("closed")
}
nat.mappings[m] = struct{}{}
nat.refCount.Add(1)
nat.mappingmu.Unlock()
go nat.refreshMappings(m)

// do it once synchronously, so first mapping is done right away, and before exiting,
// allowing users -- in the optimistic case -- to use results right after.
nat.establishMapping(m)
return m, nil
}

func (nat *NAT) removeMapping(m *mapping) {
nat.mappingmu.Lock()
delete(nat.mappings, m)
nat.mappingmu.Unlock()
nat.natmu.Lock()
nat.nat.DeletePortMapping(m.Protocol(), m.InternalPort())
nat.natmu.Unlock()
}

func (nat *NAT) refreshMappings(m *mapping) {
defer nat.refCount.Done()
t := time.NewTicker(MappingDuration / 3)
defer t.Stop()

for {
select {
case <-t.C:
nat.establishMapping(m)
case <-nat.ctx.Done():
m.Close()
return
}
}
}

func (nat *NAT) establishMapping(m *mapping) {
oldport := m.ExternalPort()

log.Debugf("Attempting port map: %s/%d", m.Protocol(), m.InternalPort())
const comment = "libp2p"

nat.natmu.Lock()
newport, err := nat.nat.AddPortMapping(m.Protocol(), m.InternalPort(), comment, MappingDuration)
if err != nil {
// Some hardware does not support mappings with timeout, so try that
newport, err = nat.nat.AddPortMapping(m.Protocol(), m.InternalPort(), comment, 0)
}
nat.natmu.Unlock()

if err != nil || newport == 0 {
m.setExternalPort(0) // clear mapping
// TODO: log.Event
if err != nil {
log.Warnf("failed to establish port mapping: %s", err)
} else {
log.Warnf("failed to establish port mapping: newport = 0")
}
// we do not close if the mapping failed,
// because it may work again next time.
return
}

m.setExternalPort(newport)
log.Debugf("NAT Mapping: %d --> %d (%s)", m.ExternalPort(), m.InternalPort(), m.Protocol())
if oldport != 0 && newport != oldport {
log.Debugf("failed to renew same port mapping: ch %d -> %d", oldport, newport)
}
}