Skip to content

Commit 3617750

Browse files
committed
better nat mapping
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.
1 parent df7e2af commit 3617750

File tree

7 files changed

+299
-132
lines changed

7 files changed

+299
-132
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ require (
1919
github.com/libp2p/go-libp2p-interface-pnet v0.0.1
2020
github.com/libp2p/go-libp2p-loggables v0.0.1
2121
github.com/libp2p/go-libp2p-metrics v0.0.1
22-
github.com/libp2p/go-libp2p-nat v0.0.1
22+
github.com/libp2p/go-libp2p-nat v0.0.2
2323
github.com/libp2p/go-libp2p-net v0.0.1
2424
github.com/libp2p/go-libp2p-netutil v0.0.1
2525
github.com/libp2p/go-libp2p-peer v0.0.1

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ github.com/libp2p/go-libp2p-metrics v0.0.1 h1:yumdPC/P2VzINdmcKZd0pciSUCpou+s0lw
108108
github.com/libp2p/go-libp2p-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08=
109109
github.com/libp2p/go-libp2p-nat v0.0.1 h1:on/zju7XE+JXc8gH+vTKmIh2UJFC1K8kGnJYluQrlz4=
110110
github.com/libp2p/go-libp2p-nat v0.0.1/go.mod h1:4L6ajyUIlJvx1Cbh5pc6Ma6vMDpKXf3GgLO5u7W0oQ4=
111+
github.com/libp2p/go-libp2p-nat v0.0.2 h1:sKI5hiCsGFhuEKdXMsF9mywQu2qhfoIGX6a+VG6zelE=
112+
github.com/libp2p/go-libp2p-nat v0.0.2/go.mod h1:QrjXQSD5Dj4IJOdEcjHRkWTSomyxRo6HnUkf/TfQpLQ=
111113
github.com/libp2p/go-libp2p-net v0.0.1 h1:xJ4Vh4yKF/XKb8fd1Ev0ebAGzVjMxXzrxG2kjtU+F5Q=
112114
github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c=
113115
github.com/libp2p/go-libp2p-netutil v0.0.1 h1:LgD6+skofkOx8z6odD9+MZHKjupv3ng1u6KRhaADTnA=

p2p/host/basic/basic_host.go

+139-17
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package basichost
33
import (
44
"context"
55
"io"
6+
"net"
67
"time"
78

89
logging "github.com/ipfs/go-log"
910
goprocess "github.com/jbenet/goprocess"
1011
goprocessctx "github.com/jbenet/goprocess/context"
1112
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
13+
inat "github.com/libp2p/go-libp2p-nat"
1214
inet "github.com/libp2p/go-libp2p-net"
1315
peer "github.com/libp2p/go-libp2p-peer"
1416
pstore "github.com/libp2p/go-libp2p-peerstore"
@@ -17,6 +19,7 @@ import (
1719
ping "github.com/libp2p/go-libp2p/p2p/protocol/ping"
1820
ma "github.com/multiformats/go-multiaddr"
1921
madns "github.com/multiformats/go-multiaddr-dns"
22+
manet "github.com/multiformats/go-multiaddr-net"
2023
msmux "github.com/multiformats/go-multistream"
2124
)
2225

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

487490
// mergeAddrs merges input address lists, leave only unique addresses
488-
func mergeAddrs(addrLists ...[]ma.Multiaddr) (uniqueAddrs []ma.Multiaddr) {
491+
func dedupAddrs(addrs []ma.Multiaddr) (uniqueAddrs []ma.Multiaddr) {
489492
exists := make(map[string]bool)
490-
for _, addrList := range addrLists {
491-
for _, addr := range addrList {
492-
k := string(addr.Bytes())
493-
if exists[k] {
494-
continue
495-
}
496-
exists[k] = true
497-
uniqueAddrs = append(uniqueAddrs, addr)
493+
for _, addr := range addrs {
494+
k := string(addr.Bytes())
495+
if exists[k] {
496+
continue
498497
}
498+
exists[k] = true
499+
uniqueAddrs = append(uniqueAddrs, addr)
499500
}
500501
return uniqueAddrs
501502
}
@@ -507,19 +508,140 @@ func (h *BasicHost) AllAddrs() []ma.Multiaddr {
507508
if err != nil {
508509
log.Debug("error retrieving network interface addrs")
509510
}
510-
var observedAddrs []ma.Multiaddr
511-
if h.ids != nil {
512-
// peer observed addresses
513-
observedAddrs = h.ids.OwnObservedAddrs()
514-
}
515-
var natAddrs []ma.Multiaddr
511+
var natMappings []inat.Mapping
512+
516513
// natmgr is nil if we do not use nat option;
517514
// h.natmgr.NAT() is nil if not ready, or no nat is available.
518515
if h.natmgr != nil && h.natmgr.NAT() != nil {
519-
natAddrs = h.natmgr.NAT().ExternalAddrs()
516+
natMappings = h.natmgr.NAT().Mappings()
520517
}
521518

522-
return mergeAddrs(listenAddrs, observedAddrs, natAddrs)
519+
finalAddrs := listenAddrs
520+
if len(natMappings) > 0 {
521+
522+
// We have successfully mapped ports on our NAT. Use those
523+
// instead of observed addresses (mostly).
524+
525+
// First, generate a mapping table.
526+
// protocol -> internal port -> external addr
527+
ports := make(map[string]map[int]net.Addr)
528+
for _, m := range natMappings {
529+
addr, err := m.ExternalAddr()
530+
if err != nil {
531+
// mapping not ready yet.
532+
continue
533+
}
534+
protoPorts, ok := ports[m.Protocol()]
535+
if !ok {
536+
protoPorts = make(map[int]net.Addr)
537+
ports[m.Protocol()] = protoPorts
538+
}
539+
protoPorts[m.InternalPort()] = addr
540+
}
541+
542+
// Next, apply this mapping to our addresses.
543+
for _, listen := range listenAddrs {
544+
found := false
545+
transport, rest := ma.SplitFunc(listen, func(c ma.Component) bool {
546+
if found {
547+
return true
548+
}
549+
switch c.Protocol().Code {
550+
case ma.P_TCP, ma.P_UDP:
551+
found = true
552+
}
553+
return false
554+
})
555+
if !manet.IsThinWaist(transport) {
556+
continue
557+
}
558+
559+
naddr, err := manet.ToNetAddr(transport)
560+
if err != nil {
561+
log.Error("error parsing net multiaddr %q: %s", transport, err)
562+
continue
563+
}
564+
565+
var (
566+
ip net.IP
567+
iport int
568+
protocol string
569+
)
570+
switch naddr := naddr.(type) {
571+
case *net.TCPAddr:
572+
ip = naddr.IP
573+
iport = naddr.Port
574+
protocol = "tcp"
575+
case *net.UDPAddr:
576+
ip = naddr.IP
577+
iport = naddr.Port
578+
protocol = "udp"
579+
default:
580+
continue
581+
}
582+
583+
if !ip.IsGlobalUnicast() {
584+
// We only map global unicast ports.
585+
continue
586+
}
587+
588+
mappedAddr, ok := ports[protocol][iport]
589+
if !ok {
590+
// Not mapped.
591+
continue
592+
}
593+
594+
mappedMaddr, err := manet.FromNetAddr(mappedAddr)
595+
if err != nil {
596+
log.Errorf("mapped addr can't be turned into a multiaddr %q: %s", mappedAddr, err)
597+
continue
598+
}
599+
600+
// Did the router give us a routable public addr?
601+
if manet.IsPublicAddr(mappedMaddr) {
602+
// Yes, use it.
603+
extMaddr := mappedMaddr
604+
if rest != nil {
605+
extMaddr = ma.Join(extMaddr, rest)
606+
}
607+
608+
// Add in the mapped addr.
609+
finalAddrs = append(finalAddrs, extMaddr)
610+
continue
611+
}
612+
613+
// No. Ok, let's try our observed addresses.
614+
615+
// Now, check if we have any observed addresses that
616+
// differ from the one reported by the router. Routers
617+
// don't always give the most accurate information.
618+
observed := h.ids.ObservedAddrsFor(listen)
619+
620+
if len(observed) == 0 {
621+
continue
622+
}
623+
624+
// Drop the IP from the external maddr
625+
_, extMaddrNoIP := ma.SplitFirst(mappedMaddr)
626+
627+
for _, obsMaddr := range observed {
628+
// Extract a public observed addr.
629+
ip, _ := ma.SplitFirst(obsMaddr)
630+
if ip == nil || !manet.IsPublicAddr(ip) {
631+
continue
632+
}
633+
634+
finalAddrs = append(finalAddrs, ma.Join(ip, extMaddrNoIP))
635+
}
636+
}
637+
} else {
638+
var observedAddrs []ma.Multiaddr
639+
if h.ids != nil {
640+
observedAddrs = h.ids.OwnObservedAddrs()
641+
}
642+
finalAddrs = append(finalAddrs, observedAddrs...)
643+
}
644+
return dedupAddrs(finalAddrs)
523645
}
524646

525647
// Close shuts down the Host's services (network, etc).

0 commit comments

Comments
 (0)