Skip to content

Commit

Permalink
better nat mapping
Browse files Browse the repository at this point in the history
1. Update to work with libp2p/go-libp2p-nat#14.
2. Avoid observed addrs when our NAT tells us about external addrs.
3. Ignore bad addrs reported by our NAT. Substitute with observed addrs.
4. Fix #428.
  • Loading branch information
Stebalien committed Mar 6, 2019
1 parent 9b81727 commit 90576ba
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 132 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/libp2p/go-libp2p-interface-pnet v0.0.1
github.com/libp2p/go-libp2p-loggables v0.0.1
github.com/libp2p/go-libp2p-metrics v0.0.1
github.com/libp2p/go-libp2p-nat v0.0.1
github.com/libp2p/go-libp2p-nat v0.0.2
github.com/libp2p/go-libp2p-net v0.0.1
github.com/libp2p/go-libp2p-netutil v0.0.1
github.com/libp2p/go-libp2p-peer v0.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ github.com/libp2p/go-libp2p-metrics v0.0.1 h1:yumdPC/P2VzINdmcKZd0pciSUCpou+s0lw
github.com/libp2p/go-libp2p-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08=
github.com/libp2p/go-libp2p-nat v0.0.1 h1:on/zju7XE+JXc8gH+vTKmIh2UJFC1K8kGnJYluQrlz4=
github.com/libp2p/go-libp2p-nat v0.0.1/go.mod h1:4L6ajyUIlJvx1Cbh5pc6Ma6vMDpKXf3GgLO5u7W0oQ4=
github.com/libp2p/go-libp2p-nat v0.0.2 h1:sKI5hiCsGFhuEKdXMsF9mywQu2qhfoIGX6a+VG6zelE=
github.com/libp2p/go-libp2p-nat v0.0.2/go.mod h1:QrjXQSD5Dj4IJOdEcjHRkWTSomyxRo6HnUkf/TfQpLQ=
github.com/libp2p/go-libp2p-net v0.0.1 h1:xJ4Vh4yKF/XKb8fd1Ev0ebAGzVjMxXzrxG2kjtU+F5Q=
github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c=
github.com/libp2p/go-libp2p-netutil v0.0.1 h1:LgD6+skofkOx8z6odD9+MZHKjupv3ng1u6KRhaADTnA=
Expand Down
156 changes: 139 additions & 17 deletions p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package basichost
import (
"context"
"io"
"net"
"time"

logging "github.com/ipfs/go-log"
goprocess "github.com/jbenet/goprocess"
goprocessctx "github.com/jbenet/goprocess/context"
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
inat "github.com/libp2p/go-libp2p-nat"
inet "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer"
pstore "github.com/libp2p/go-libp2p-peerstore"
Expand All @@ -17,6 +19,7 @@ import (
ping "github.com/libp2p/go-libp2p/p2p/protocol/ping"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
manet "github.com/multiformats/go-multiaddr-net"
msmux "github.com/multiformats/go-multistream"
)

Expand Down Expand Up @@ -485,17 +488,15 @@ func (h *BasicHost) Addrs() []ma.Multiaddr {
}

// mergeAddrs merges input address lists, leave only unique addresses
func mergeAddrs(addrLists ...[]ma.Multiaddr) (uniqueAddrs []ma.Multiaddr) {
func dedupAddrs(addrs []ma.Multiaddr) (uniqueAddrs []ma.Multiaddr) {
exists := make(map[string]bool)
for _, addrList := range addrLists {
for _, addr := range addrList {
k := string(addr.Bytes())
if exists[k] {
continue
}
exists[k] = true
uniqueAddrs = append(uniqueAddrs, addr)
for _, addr := range addrs {
k := string(addr.Bytes())
if exists[k] {
continue
}
exists[k] = true
uniqueAddrs = append(uniqueAddrs, addr)
}
return uniqueAddrs
}
Expand All @@ -507,19 +508,140 @@ func (h *BasicHost) AllAddrs() []ma.Multiaddr {
if err != nil {
log.Debug("error retrieving network interface addrs")
}
var observedAddrs []ma.Multiaddr
if h.ids != nil {
// peer observed addresses
observedAddrs = h.ids.OwnObservedAddrs()
}
var natAddrs []ma.Multiaddr
var natMappings []inat.Mapping

// natmgr is nil if we do not use nat option;
// h.natmgr.NAT() is nil if not ready, or no nat is available.
if h.natmgr != nil && h.natmgr.NAT() != nil {
natAddrs = h.natmgr.NAT().ExternalAddrs()
natMappings = h.natmgr.NAT().Mappings()
}

return mergeAddrs(listenAddrs, observedAddrs, natAddrs)
finalAddrs := listenAddrs
if len(natMappings) > 0 {

// We have successfully mapped ports on our NAT. Use those
// instead of observed addresses (mostly).

// First, generate a mapping table.
// protocol -> internal port -> external addr
ports := make(map[string]map[int]net.Addr)
for _, m := range natMappings {
addr, err := m.ExternalAddr()
if err != nil {
// mapping not ready yet.
continue
}
protoPorts, ok := ports[m.Protocol()]
if !ok {
protoPorts = make(map[int]net.Addr)
ports[m.Protocol()] = protoPorts
}
protoPorts[m.InternalPort()] = addr
}

// Next, apply this mapping to our addresses.
for _, listen := range listenAddrs {
found := false
transport, rest := ma.SplitFunc(listen, func(c ma.Component) bool {
if found {
return true
}
switch c.Protocol().Code {
case ma.P_TCP, ma.P_UDP:
found = true
}
return false
})
if !manet.IsThinWaist(transport) {
continue
}

naddr, err := manet.ToNetAddr(transport)
if err != nil {
log.Error("error parsing net multiaddr %q: %s", transport, err)
continue
}

var (
ip net.IP
iport int
protocol string
)
switch naddr := naddr.(type) {
case *net.TCPAddr:
ip = naddr.IP
iport = naddr.Port
protocol = "tcp"
case *net.UDPAddr:
ip = naddr.IP
iport = naddr.Port
protocol = "udp"
default:
continue
}

if !ip.IsGlobalUnicast() {
// We only map global unicast ports.
continue
}

mappedAddr, ok := ports[protocol][iport]
if !ok {
// Not mapped.
continue
}

mappedMaddr, err := manet.FromNetAddr(mappedAddr)
if err != nil {
log.Errorf("mapped addr can't be turned into a multiaddr %q: %s", mappedAddr, err)
continue
}

// Did the router give us a routable public addr?
if manet.IsPublicAddr(mappedMaddr) {
// Yes, use it.
extMaddr := mappedMaddr
if rest != nil {
extMaddr = ma.Join(extMaddr, rest)
}

// Add in the mapped addr.
finalAddrs = append(finalAddrs, extMaddr)
continue
}

// No. Ok, let's try our observed addresses.

// Now, check if we have any observed addresses that
// differ from the one reported by the router. Routers
// don't always give the most accurate information.
observed := h.ids.ObservedAddrsFor(listen)

if len(observed) == 0 {
continue
}

// Drop the IP from the external maddr
_, extMaddrNoIP := ma.SplitFirst(mappedMaddr)

for _, obsMaddr := range observed {
// Extract a public observed addr.
ip, _ := ma.SplitFirst(obsMaddr)
if ip == nil || !manet.IsPublicAddr(ip) {
continue
}

finalAddrs = append(finalAddrs, ma.Join(ip, extMaddrNoIP))
}
}
} else {
var observedAddrs []ma.Multiaddr
if h.ids != nil {
observedAddrs = h.ids.OwnObservedAddrs()
}
finalAddrs = append(finalAddrs, observedAddrs...)
}
return dedupAddrs(finalAddrs)
}

// Close shuts down the Host's services (network, etc).
Expand Down
Loading

0 comments on commit 90576ba

Please sign in to comment.