Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
go-libp2p
Commits
0e7e451d
Commit
0e7e451d
authored
Oct 24, 2016
by
Jeromy
Browse files
extract NAT traversal code to its own lib
parent
c859385c
Changes
6
Hide whitespace changes
Inline
Side-by-side
p2p/host/basic/natmgr.go
View file @
0e7e451d
...
...
@@ -4,7 +4,7 @@ import (
"context"
"sync"
inat
"github.com/libp2p/go-libp2p
/p2p/
nat"
inat
"github.com/libp2p/go-libp2p
-
nat"
goprocess
"github.com/jbenet/goprocess"
lgbl
"github.com/libp2p/go-libp2p-loggables"
...
...
p2p/nat/mapping.go
deleted
100644 → 0
View file @
c859385c
package
nat
import
(
"fmt"
"sync"
"time"
"github.com/jbenet/goprocess"
ma
"github.com/multiformats/go-multiaddr"
manet
"github.com/multiformats/go-multiaddr-net"
)
// 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
// InternalAddr returns the internal address.
InternalAddr
()
ma
.
Multiaddr
// ExternalAddr returns the external facing address. If the mapping is not
// established, addr will be nil, and and ErrNoMapping will be returned.
ExternalAddr
()
(
addr
ma
.
Multiaddr
,
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
permanent
bool
intaddr
ma
.
Multiaddr
proc
goprocess
.
Process
comment
string
cached
ma
.
Multiaddr
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
)
InternalAddr
()
ma
.
Multiaddr
{
m
.
Lock
()
defer
m
.
Unlock
()
return
m
.
intaddr
}
func
(
m
*
mapping
)
ExternalAddr
()
(
ma
.
Multiaddr
,
error
)
{
m
.
cacheLk
.
Lock
()
ctime
:=
m
.
cacheTime
cval
:=
m
.
cached
m
.
cacheLk
.
Unlock
()
if
time
.
Since
(
ctime
)
<
CacheTime
{
return
cval
,
nil
}
if
m
.
ExternalPort
()
==
0
{
// dont even try right now.
return
nil
,
ErrNoMapping
}
m
.
nat
.
natmu
.
Lock
()
ip
,
err
:=
m
.
nat
.
nat
.
GetExternalAddress
()
m
.
nat
.
natmu
.
Unlock
()
if
err
!=
nil
{
return
nil
,
err
}
ipmaddr
,
err
:=
manet
.
FromIP
(
ip
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"error parsing ip"
)
}
// call m.ExternalPort again, as mapping may have changed under our feet. (tocttou)
extport
:=
m
.
ExternalPort
()
if
extport
==
0
{
return
nil
,
ErrNoMapping
}
tcp
,
err
:=
ma
.
NewMultiaddr
(
fmt
.
Sprintf
(
"/%s/%d"
,
m
.
Protocol
(),
extport
))
if
err
!=
nil
{
return
nil
,
err
}
maddr2
:=
ipmaddr
.
Encapsulate
(
tcp
)
m
.
cacheLk
.
Lock
()
m
.
cached
=
maddr2
m
.
cacheTime
=
time
.
Now
()
m
.
cacheLk
.
Unlock
()
return
maddr2
,
nil
}
func
(
m
*
mapping
)
Close
()
error
{
return
m
.
proc
.
Close
()
}
p2p/nat/nat.go
deleted
100644 → 0
View file @
c859385c
package
nat
import
(
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
nat
"github.com/fd/go-nat"
logging
"github.com/ipfs/go-log"
goprocess
"github.com/jbenet/goprocess"
periodic
"github.com/jbenet/goprocess/periodic"
ma
"github.com/multiformats/go-multiaddr"
manet
"github.com/multiformats/go-multiaddr-net"
)
var
(
// ErrNoMapping signals no mapping exists for an address
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
()
*
NAT
{
nat
,
err
:=
nat
.
DiscoverGateway
()
if
err
!=
nil
{
log
.
Debug
(
"DiscoverGateway error:"
,
err
)
return
nil
}
addr
,
err
:=
nat
.
GetDeviceAddress
()
if
err
!=
nil
{
log
.
Debug
(
"DiscoverGateway address error:"
,
err
)
}
else
{
log
.
Debug
(
"DiscoverGateway address:"
,
addr
)
}
return
newNAT
(
nat
)
}
// 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
proc
goprocess
.
Process
// manages nat mappings lifecycle
mappingmu
sync
.
RWMutex
// guards mappings
mappings
map
[
*
mapping
]
struct
{}
Notifier
}
func
newNAT
(
realNAT
nat
.
NAT
)
*
NAT
{
return
&
NAT
{
nat
:
realNAT
,
proc
:
goprocess
.
WithParent
(
goprocess
.
Background
()),
mappings
:
make
(
map
[
*
mapping
]
struct
{}),
}
}
// Close shuts down all port mappings. NAT can no longer be used.
func
(
nat
*
NAT
)
Close
()
error
{
return
nat
.
proc
.
Close
()
}
// Process returns the nat's life-cycle manager, for making it listen
// to close signals.
func
(
nat
*
NAT
)
Process
()
goprocess
.
Process
{
return
nat
.
proc
}
// 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
}
func
(
nat
*
NAT
)
addMapping
(
m
*
mapping
)
{
// make mapping automatically close when nat is closed.
nat
.
proc
.
AddChild
(
m
.
proc
)
nat
.
mappingmu
.
Lock
()
nat
.
mappings
[
m
]
=
struct
{}{}
nat
.
mappingmu
.
Unlock
()
}
func
(
nat
*
NAT
)
rmMapping
(
m
*
mapping
)
{
nat
.
mappingmu
.
Lock
()
delete
(
nat
.
mappings
,
m
)
nat
.
mappingmu
.
Unlock
()
}
// NewMapping attemps 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
(
maddr
ma
.
Multiaddr
)
(
Mapping
,
error
)
{
if
nat
==
nil
{
return
nil
,
fmt
.
Errorf
(
"no nat available"
)
}
network
,
addr
,
err
:=
manet
.
DialArgs
(
maddr
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"DialArgs failed on addr:"
,
maddr
.
String
())
}
switch
network
{
case
"tcp"
,
"tcp4"
,
"tcp6"
:
network
=
"tcp"
case
"udp"
,
"udp4"
,
"udp6"
:
network
=
"udp"
default
:
return
nil
,
fmt
.
Errorf
(
"transport not supported by NAT: %s"
,
network
)
}
intports
:=
strings
.
Split
(
addr
,
":"
)[
1
]
intport
,
err
:=
strconv
.
Atoi
(
intports
)
if
err
!=
nil
{
return
nil
,
err
}
m
:=
&
mapping
{
nat
:
nat
,
proto
:
network
,
intport
:
intport
,
intaddr
:
maddr
,
}
m
.
proc
=
goprocess
.
WithTeardown
(
func
()
error
{
nat
.
rmMapping
(
m
)
return
nil
})
nat
.
addMapping
(
m
)
m
.
proc
.
AddChild
(
periodic
.
Every
(
MappingDuration
/
3
,
func
(
worker
goprocess
.
Process
)
{
nat
.
establishMapping
(
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
)
establishMapping
(
m
*
mapping
)
{
oldport
:=
m
.
ExternalPort
()
log
.
Debugf
(
"Attempting port map: %s/%d"
,
m
.
Protocol
(),
m
.
InternalPort
())
comment
:=
"libp2p"
if
m
.
comment
!=
""
{
comment
=
"libp2p-"
+
m
.
comment
}
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
()
failure
:=
func
()
{
m
.
setExternalPort
(
0
)
// clear mapping
// TODO: log.Event
log
.
Warningf
(
"failed to establish port mapping: %s"
,
err
)
nat
.
Notifier
.
notifyAll
(
func
(
n
Notifiee
)
{
n
.
MappingFailed
(
nat
,
m
,
oldport
,
err
)
})
// we do not close if the mapping failed,
// because it may work again next time.
}
if
err
!=
nil
||
newport
==
0
{
failure
()
return
}
m
.
setExternalPort
(
newport
)
ext
,
err
:=
m
.
ExternalAddr
()
if
err
!=
nil
{
log
.
Debugf
(
"NAT Mapping addr error: %s %s"
,
m
.
InternalAddr
(),
err
)
failure
()
return
}
log
.
Debugf
(
"NAT Mapping: %s --> %s"
,
m
.
InternalAddr
(),
ext
)
if
oldport
!=
0
&&
newport
!=
oldport
{
log
.
Debugf
(
"failed to renew same port mapping: ch %d -> %d"
,
oldport
,
newport
)
nat
.
Notifier
.
notifyAll
(
func
(
n
Notifiee
)
{
n
.
MappingChanged
(
nat
,
m
,
oldport
,
newport
)
})
}
nat
.
Notifier
.
notifyAll
(
func
(
n
Notifiee
)
{
n
.
MappingSuccess
(
nat
,
m
)
})
}
// PortMapAddrs attempts to open (and continue to keep open)
// port mappings for given addrs. This function blocks until
// all addresses have been tried. This allows clients to
// retrieve results immediately after:
//
// nat.PortMapAddrs(addrs)
// mapped := nat.ExternalAddrs()
//
// Some 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
)
PortMapAddrs
(
addrs
[]
ma
.
Multiaddr
)
{
// spin off addr mappings independently.
var
wg
sync
.
WaitGroup
for
_
,
addr
:=
range
addrs
{
// do all of them concurrently
wg
.
Add
(
1
)
go
func
()
{
defer
wg
.
Done
()
nat
.
NewMapping
(
addr
)
}()
}
wg
.
Wait
()
}
// MappedAddrs returns address mappings NAT believes have been
// successfully established. Unsuccessful mappings are nil. This is:
//
// map[internalAddr]externalAddr
//
// This set of mappings _may not_ be correct, as NAT devices are finicky.
// Consider this with _best effort_ semantics.
func
(
nat
*
NAT
)
MappedAddrs
()
map
[
ma
.
Multiaddr
]
ma
.
Multiaddr
{
mappings
:=
nat
.
Mappings
()
addrmap
:=
make
(
map
[
ma
.
Multiaddr
]
ma
.
Multiaddr
,
len
(
mappings
))
for
_
,
m
:=
range
mappings
{
i
:=
m
.
InternalAddr
()
e
,
err
:=
m
.
ExternalAddr
()
if
err
!=
nil
{
addrmap
[
i
]
=
nil
}
else
{
addrmap
[
i
]
=
e
}
}
return
addrmap
}
// ExternalAddrs returns a list of addresses that NAT believes have
// been successfully established. Unsuccessful mappings are omitted,
// so nat.ExternalAddrs() may return less addresses than nat.InternalAddrs().
// To see which addresses are mapped, use nat.MappedAddrs().
//
// This set of mappings _may not_ be correct, as NAT devices are finicky.
// Consider this with _best effort_ semantics.
func
(
nat
*
NAT
)
ExternalAddrs
()
[]
ma
.
Multiaddr
{
mappings
:=
nat
.
Mappings
()
addrs
:=
make
([]
ma
.
Multiaddr
,
0
,
len
(
mappings
))
for
_
,
m
:=
range
mappings
{
a
,
err
:=
m
.
ExternalAddr
()
if
err
!=
nil
{
continue
// this mapping not currently successful.
}
addrs
=
append
(
addrs
,
a
)
}
return
addrs
}
p2p/nat/notifier.go
deleted
100644 → 0
View file @
c859385c
package
nat
import
(
notifier
"github.com/whyrusleeping/go-notifier"
)
// Notifier is an object that assists NAT in notifying listeners.
// It is implemented using thirdparty/notifier
type
Notifier
struct
{
n
notifier
.
Notifier
}
func
(
n
*
Notifier
)
notifyAll
(
notify
func
(
n
Notifiee
))
{
n
.
n
.
NotifyAll
(
func
(
n
notifier
.
Notifiee
)
{
notify
(
n
.
(
Notifiee
))
})
}
// Notify signs up notifiee to listen to NAT events.
func
(
n
*
Notifier
)
Notify
(
notifiee
Notifiee
)
{
n
.
n
.
Notify
(
n
)
}
// StopNotify stops signaling events to notifiee.
func
(
n
*
Notifier
)
StopNotify
(
notifiee
Notifiee
)
{
n
.
n
.
StopNotify
(
notifiee
)
}
// Notifiee is an interface objects must implement to listen to NAT events.
type
Notifiee
interface
{
// Called every time a successful mapping happens
// Warning: the port mapping may have changed. If that is the
// case, both MappingSuccess and MappingChanged are called.
MappingSuccess
(
nat
*
NAT
,
m
Mapping
)
// Called when mapping a port succeeds, but the mapping is
// with a different port than an earlier success.
MappingChanged
(
nat
*
NAT
,
m
Mapping
,
oldport
int
,
newport
int
)
// Called when a port mapping fails. NAT will continue attempting after
// the next period. To stop trying, use: mapping.Close(). After this failure,
// mapping.ExternalPort() will be zero, and nat.ExternalAddrs() will not
// return the address for this mapping. With luck, the next attempt will
// succeed, without the client needing to do anything.
MappingFailed
(
nat
*
NAT
,
m
Mapping
,
oldport
int
,
err
error
)
}
p2p/protocol/relay/relay.go
View file @
0e7e451d
...
...
@@ -9,10 +9,10 @@ import (
host
"github.com/libp2p/go-libp2p-host"
logging
"github.com/ipfs/go-log"
mh
"github.com/jbenet/go-multihash"
inet
"github.com/libp2p/go-libp2p-net"
peer
"github.com/libp2p/go-libp2p-peer"
protocol
"github.com/libp2p/go-libp2p-protocol"
mh
"github.com/multiformats/go-multihash"
)
var
log
=
logging
.
Logger
(
"protocol/relay"
)
...
...
package.json
View file @
0e7e451d
...
...
@@ -38,11 +38,6 @@
"name"
:
"go-multistream"
,
"version"
:
"0.3.1"
},
{
"hash"
:
"QmYabcy8kaP658zZRZHLqRquJ37ycNWr4qhBL25tUodZWc"
,
"name"
:
"go-nat"
,
"version"
:
"0.0.0"
},
{
"hash"
:
"QmQHGMVmrsgmqUG8ih3puNXUJneSpi13dkcZpzLKkskUkH"
,
"name"
:
"go-detect-race"
,
...
...
@@ -248,6 +243,12 @@
"hash"
:
"QmeAfPWBWDQq9qjQ5oiWhaFs7oEsfB6FyEj5VxNdc2r34q"
,
"name"
:
"go-libp2p-swarm"
,
"version"
:
"1.0.0"
},
{
"author"
:
"whyrusleeping"
,
"hash"
:
"QmPpncQ3L4bC3rnwLBrgEomygs5RbnFejb68GgsecxbMiL"
,
"name"
:
"go-libp2p-nat"
,
"version"
:
"0.0.0"
}
],
"gxVersion"
:
"0.4.0"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment