mirror of
https://github.com/ipfs/kubo.git
synced 2025-09-13 17:20:27 +08:00
p2p/nat: upnp + pmp
This commit is contained in:
24
core/core.go
24
core/core.go
@ -18,6 +18,7 @@ import (
|
|||||||
ic "github.com/jbenet/go-ipfs/p2p/crypto"
|
ic "github.com/jbenet/go-ipfs/p2p/crypto"
|
||||||
p2phost "github.com/jbenet/go-ipfs/p2p/host"
|
p2phost "github.com/jbenet/go-ipfs/p2p/host"
|
||||||
p2pbhost "github.com/jbenet/go-ipfs/p2p/host/basic"
|
p2pbhost "github.com/jbenet/go-ipfs/p2p/host/basic"
|
||||||
|
inat "github.com/jbenet/go-ipfs/p2p/nat"
|
||||||
swarm "github.com/jbenet/go-ipfs/p2p/net/swarm"
|
swarm "github.com/jbenet/go-ipfs/p2p/net/swarm"
|
||||||
addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
|
addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
|
||||||
peer "github.com/jbenet/go-ipfs/p2p/peer"
|
peer "github.com/jbenet/go-ipfs/p2p/peer"
|
||||||
@ -389,15 +390,13 @@ func loadPrivateKey(cfg *config.Identity, id peer.ID) (ic.PrivKey, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
|
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
|
||||||
|
var listen []ma.Multiaddr
|
||||||
var err error
|
for _, addr := range cfg.Addresses.Swarm {
|
||||||
listen := make([]ma.Multiaddr, len(cfg.Addresses.Swarm))
|
maddr, err := ma.NewMultiaddr(addr)
|
||||||
for i, addr := range cfg.Addresses.Swarm {
|
|
||||||
|
|
||||||
listen[i], err = ma.NewMultiaddr(addr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm[%d]: %s", i, cfg.Addresses.Swarm)
|
return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
|
||||||
}
|
}
|
||||||
|
listen = append(listen, maddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return listen, nil
|
return listen, nil
|
||||||
@ -413,7 +412,7 @@ func constructPeerHost(ctx context.Context, cfg *config.Config, id peer.ID, ps p
|
|||||||
// make sure we error out if our config does not have addresses we can use
|
// make sure we error out if our config does not have addresses we can use
|
||||||
log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
|
log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
|
||||||
filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
|
filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
|
||||||
log.Debugf("Config.Addresses.Swarm:%s (filtered)", listenAddrs)
|
log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
|
||||||
if len(filteredAddrs) < 1 {
|
if len(filteredAddrs) < 1 {
|
||||||
return nil, debugerror.Errorf("addresses in config not usable: %s", listenAddrs)
|
return nil, debugerror.Errorf("addresses in config not usable: %s", listenAddrs)
|
||||||
}
|
}
|
||||||
@ -431,7 +430,14 @@ func constructPeerHost(ctx context.Context, cfg *config.Config, id peer.ID, ps p
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, debugerror.Wrap(err)
|
return nil, debugerror.Wrap(err)
|
||||||
}
|
}
|
||||||
log.Info("Swarm listening at: %s", addrs)
|
log.Infof("Swarm listening at: %s", addrs)
|
||||||
|
|
||||||
|
mapAddrs := inat.MapAddrs(filteredAddrs)
|
||||||
|
if len(mapAddrs) > 0 {
|
||||||
|
log.Infof("NAT mapping addrs: %s", mapAddrs)
|
||||||
|
addrs = append(addrs, mapAddrs...)
|
||||||
|
}
|
||||||
|
|
||||||
ps.AddAddresses(id, addrs)
|
ps.AddAddresses(id, addrs)
|
||||||
return peerhost, nil
|
return peerhost, nil
|
||||||
}
|
}
|
||||||
|
168
p2p/nat/nat.go
Normal file
168
p2p/nat/nat.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package nat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
|
||||||
|
manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
|
||||||
|
|
||||||
|
nat "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/fd/go-nat"
|
||||||
|
goprocess "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
||||||
|
eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = eventlog.Logger("nat")
|
||||||
|
|
||||||
|
const MappingDuration = time.Second * 60
|
||||||
|
|
||||||
|
func DiscoverGateway() nat.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 nat
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mapping interface {
|
||||||
|
NAT() nat.NAT
|
||||||
|
Protocol() string
|
||||||
|
InternalPort() int
|
||||||
|
ExternalPort() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapping struct {
|
||||||
|
// keeps republishing
|
||||||
|
nat nat.NAT
|
||||||
|
proto string
|
||||||
|
intport int
|
||||||
|
extport int
|
||||||
|
proc goprocess.Process
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mapping) NAT() nat.NAT {
|
||||||
|
return m.nat
|
||||||
|
}
|
||||||
|
func (m *mapping) Protocol() string {
|
||||||
|
return m.proto
|
||||||
|
}
|
||||||
|
func (m *mapping) InternalPort() int {
|
||||||
|
return m.intport
|
||||||
|
}
|
||||||
|
func (m *mapping) ExternalPort() int {
|
||||||
|
return m.extport
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMapping attemps to construct a mapping on protocl and internal port
|
||||||
|
func NewMapping(nat nat.NAT, protocol string, internalPort int) (Mapping, error) {
|
||||||
|
log.Debugf("Attempting port map: %s/%d", protocol, internalPort)
|
||||||
|
eport, err := nat.AddPortMapping(protocol, internalPort, "http", MappingDuration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &mapping{
|
||||||
|
nat: nat,
|
||||||
|
proto: protocol,
|
||||||
|
intport: internalPort,
|
||||||
|
extport: eport,
|
||||||
|
}
|
||||||
|
|
||||||
|
m.proc = goprocess.Go(func(worker goprocess.Process) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-worker.Closing():
|
||||||
|
return
|
||||||
|
case <-time.After(MappingDuration / 3):
|
||||||
|
eport, err := m.NAT().AddPortMapping(protocol, internalPort, "http", MappingDuration)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to renew port mapping: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if eport != m.extport {
|
||||||
|
log.Warningf("failed to renew same port mapping: ch %d -> %d", m.extport, eport)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mapping) Close() error {
|
||||||
|
return m.proc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapAddr(n nat.NAT, maddr ma.Multiaddr) (ma.Multiaddr, error) {
|
||||||
|
if n == nil {
|
||||||
|
return nil, fmt.Errorf("no nat available")
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := n.GetExternalAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ipmaddr, err := manet.FromIP(ip)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
port := strings.Split(addr, ":")[1]
|
||||||
|
intport, err := strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := NewMapping(n, "tcp", intport)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", m.ExternalPort()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
maddr2 := ipmaddr.Encapsulate(tcp)
|
||||||
|
log.Debugf("NAT Mapping: %s --> %s", maddr, maddr2)
|
||||||
|
return maddr2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||||
|
nat := DiscoverGateway()
|
||||||
|
|
||||||
|
var advertise []ma.Multiaddr
|
||||||
|
for _, maddr := range addrs {
|
||||||
|
maddr2, err := MapAddr(nat, maddr)
|
||||||
|
if err != nil || maddr2 == nil {
|
||||||
|
log.Debug("failed to map addr:", maddr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
advertise = append(advertise, maddr2)
|
||||||
|
}
|
||||||
|
return advertise
|
||||||
|
}
|
Reference in New Issue
Block a user