Commit 33a944bc authored by Juan Batiz-Benet's avatar Juan Batiz-Benet
Browse files

p2p/nat: upnp + pmp

parent 382daf3d
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
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment