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
+}