@cryptotaxi247 / kubo / commits / 069cff3d2

p2p/nat: upnp + pmp

Juan Batiz-Benet committed Jan 24, 2015 at 12:42 UTC 069cff3d256552f04a3339a032cf771716c44fef
2 files changed +183 -9
core/core.go
+15 -9
@@ -18,6 +18,7 @@ import (
18 ic "github.com/jbenet/go-ipfs/p2p/crypto"
19 p2phost "github.com/jbenet/go-ipfs/p2p/host"
20 p2pbhost "github.com/jbenet/go-ipfs/p2p/host/basic"
21 + inat "github.com/jbenet/go-ipfs/p2p/nat"
22 swarm "github.com/jbenet/go-ipfs/p2p/net/swarm"
23 addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
24 peer "github.com/jbenet/go-ipfs/p2p/peer"
@@ -389,15 +390,13 @@ func loadPrivateKey(cfg *config.Identity, id peer.ID) (ic.PrivKey, error) {
390 }
391
392 func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
392 -
393 - var err error
394 - listen := make([]ma.Multiaddr, len(cfg.Addresses.Swarm))
395 - for i, addr := range cfg.Addresses.Swarm {
396 -
397 - listen[i], err = ma.NewMultiaddr(addr)
393 + var listen []ma.Multiaddr
394 + for _, addr := range cfg.Addresses.Swarm {
395 + maddr, err := ma.NewMultiaddr(addr)
396 if err != nil {
399 - return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm[%d]: %s", i, cfg.Addresses.Swarm)
397 + return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
398 }
399 + listen = append(listen, maddr)
400 }
401
402 return listen, nil
@@ -413,7 +412,7 @@ func constructPeerHost(ctx context.Context, cfg *config.Config, id peer.ID, ps p
412 // make sure we error out if our config does not have addresses we can use
413 log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
414 filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
416 - log.Debugf("Config.Addresses.Swarm:%s (filtered)", listenAddrs)
415 + log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
416 if len(filteredAddrs) < 1 {
417 return nil, debugerror.Errorf("addresses in config not usable: %s", listenAddrs)
418 }
@@ -431,7 +430,14 @@ func constructPeerHost(ctx context.Context, cfg *config.Config, id peer.ID, ps p
430 if err != nil {
431 return nil, debugerror.Wrap(err)
432 }
434 - log.Info("Swarm listening at: %s", addrs)
433 + log.Infof("Swarm listening at: %s", addrs)
434 +
435 + mapAddrs := inat.MapAddrs(filteredAddrs)
436 + if len(mapAddrs) > 0 {
437 + log.Infof("NAT mapping addrs: %s", mapAddrs)
438 + addrs = append(addrs, mapAddrs...)
439 + }
440 +
441 ps.AddAddresses(id, addrs)
442 return peerhost, nil
443 }
p2p/nat/nat.go new
+168
@@ -0,0 +1,168 @@
1 +package nat
2 +
3 +import (
4 + "fmt"
5 + "strconv"
6 + "strings"
7 + "time"
8 +
9 + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
10 + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
11 +
12 + nat "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/fd/go-nat"
13 + goprocess "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
14 + eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"
15 +)
16 +
17 +var log = eventlog.Logger("nat")
18 +
19 +const MappingDuration = time.Second * 60
20 +
21 +func DiscoverGateway() nat.NAT {
22 + nat, err := nat.DiscoverGateway()
23 + if err != nil {
24 + log.Debug("DiscoverGateway error:", err)
25 + return nil
26 + }
27 + addr, err := nat.GetDeviceAddress()
28 + if err != nil {
29 + log.Debug("DiscoverGateway address error:", err)
30 + } else {
31 + log.Debug("DiscoverGateway address:", addr)
32 + }
33 + return nat
34 +}
35 +
36 +type Mapping interface {
37 + NAT() nat.NAT
38 + Protocol() string
39 + InternalPort() int
40 + ExternalPort() int
41 +}
42 +
43 +type mapping struct {
44 + // keeps republishing
45 + nat nat.NAT
46 + proto string
47 + intport int
48 + extport int
49 + proc goprocess.Process
50 +}
51 +
52 +func (m *mapping) NAT() nat.NAT {
53 + return m.nat
54 +}
55 +func (m *mapping) Protocol() string {
56 + return m.proto
57 +}
58 +func (m *mapping) InternalPort() int {
59 + return m.intport
60 +}
61 +func (m *mapping) ExternalPort() int {
62 + return m.extport
63 +}
64 +
65 +// NewMapping attemps to construct a mapping on protocl and internal port
66 +func NewMapping(nat nat.NAT, protocol string, internalPort int) (Mapping, error) {
67 + log.Debugf("Attempting port map: %s/%d", protocol, internalPort)
68 + eport, err := nat.AddPortMapping(protocol, internalPort, "http", MappingDuration)
69 + if err != nil {
70 + return nil, err
71 + }
72 +
73 + m := &mapping{
74 + nat: nat,
75 + proto: protocol,
76 + intport: internalPort,
77 + extport: eport,
78 + }
79 +
80 + m.proc = goprocess.Go(func(worker goprocess.Process) {
81 + for {
82 + select {
83 + case <-worker.Closing():
84 + return
85 + case <-time.After(MappingDuration / 3):
86 + eport, err := m.NAT().AddPortMapping(protocol, internalPort, "http", MappingDuration)
87 + if err != nil {
88 + log.Warningf("failed to renew port mapping: %s", err)
89 + continue
90 + }
91 + if eport != m.extport {
92 + log.Warningf("failed to renew same port mapping: ch %d -> %d", m.extport, eport)
93 + }
94 + }
95 + }
96 + })
97 +
98 + return m, nil
99 +}
100 +
101 +func (m *mapping) Close() error {
102 + return m.proc.Close()
103 +}
104 +
105 +func MapAddr(n nat.NAT, maddr ma.Multiaddr) (ma.Multiaddr, error) {
106 + if n == nil {
107 + return nil, fmt.Errorf("no nat available")
108 + }
109 +
110 + ip, err := n.GetExternalAddress()
111 + if err != nil {
112 + return nil, err
113 + }
114 +
115 + ipmaddr, err := manet.FromIP(ip)
116 + if err != nil {
117 + return nil, fmt.Errorf("error parsing ip")
118 + }
119 +
120 + network, addr, err := manet.DialArgs(maddr)
121 + if err != nil {
122 + return nil, fmt.Errorf("DialArgs failed on addr:", maddr.String())
123 + }
124 +
125 + switch network {
126 + case "tcp", "tcp4", "tcp6":
127 + network = "tcp"
128 + case "udp", "udp4", "udp6":
129 + network = "udp"
130 + default:
131 + return nil, fmt.Errorf("transport not supported by NAT: %s", network)
132 + }
133 +
134 + port := strings.Split(addr, ":")[1]
135 + intport, err := strconv.Atoi(port)
136 + if err != nil {
137 + return nil, err
138 + }
139 +
140 + m, err := NewMapping(n, "tcp", intport)
141 + if err != nil {
142 + return nil, err
143 + }
144 +
145 + tcp, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", m.ExternalPort()))
146 + if err != nil {
147 + return nil, err
148 + }
149 +
150 + maddr2 := ipmaddr.Encapsulate(tcp)
151 + log.Debugf("NAT Mapping: %s --> %s", maddr, maddr2)
152 + return maddr2, nil
153 +}
154 +
155 +func MapAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
156 + nat := DiscoverGateway()
157 +
158 + var advertise []ma.Multiaddr
159 + for _, maddr := range addrs {
160 + maddr2, err := MapAddr(nat, maddr)
161 + if err != nil || maddr2 == nil {
162 + log.Debug("failed to map addr:", maddr, err)
163 + continue
164 + }
165 + advertise = append(advertise, maddr2)
166 + }
167 + return advertise
168 +}