swarm + handshake: better observed addr check
The check needed knowledge of the _listen_ addresses, not just the interface addresses. Also, the handshake now sends out all the addresses we accumulate about ourselves. (this may be bad in the long run, but useful now to test)
Juan Batiz-Benet committed
Nov 5, 2014 at 04:01 UTC
4989dcafedbb8ce1d8bae641d22f3b09b7a6633c
7 files changed
+105
-137
net/conn/handshake.go
+14
-56
@@ -3,15 +3,12 @@ package conn
3
import (
4
"errors"
5
"fmt"
6
- "strings"
6
7
handshake "github.com/jbenet/go-ipfs/net/handshake"
8
hspb "github.com/jbenet/go-ipfs/net/handshake/pb"
10
- u "github.com/jbenet/go-ipfs/util"
9
10
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
11
proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
14
- ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
12
)
13
14
// Handshake1 exchanges local and remote versions and compares them
@@ -62,87 +59,48 @@ func Handshake1(ctx context.Context, c Conn) error {
59
}
60
61
// Handshake3 exchanges local and remote service information
65
-func Handshake3(ctx context.Context, c Conn) error {
62
+func Handshake3(ctx context.Context, c Conn) (*handshake.Handshake3Result, error) {
63
rpeer := c.RemotePeer()
64
lpeer := c.LocalPeer()
65
66
+ // setup + send the message to remote
67
var remoteH, localH *hspb.Handshake3
70
- localH = handshake.Handshake3Msg(lpeer)
71
-
72
- rma := c.RemoteMultiaddr()
73
- localH.ObservedAddr = proto.String(rma.String())
74
-
68
+ localH = handshake.Handshake3Msg(lpeer, c.RemoteMultiaddr())
69
localB, err := proto.Marshal(localH)
70
if err != nil {
77
- return err
71
+ return nil, err
72
}
73
74
c.Out() <- localB
75
log.Debugf("Handshake1: sent to %s", rpeer)
76
77
+ // wait + listen for response
78
select {
79
case <-ctx.Done():
85
- return ctx.Err()
80
+ return nil, ctx.Err()
81
82
case <-c.Closing():
88
- return errors.New("Handshake3: error remote connection closed")
83
+ return nil, errors.New("Handshake3: error remote connection closed")
84
85
case remoteB, ok := <-c.In():
86
if !ok {
92
- return fmt.Errorf("Handshake3 error receiving from conn: %v", rpeer)
87
+ return nil, fmt.Errorf("Handshake3 error receiving from conn: %v", rpeer)
88
}
89
90
remoteH = new(hspb.Handshake3)
91
err = proto.Unmarshal(remoteB, remoteH)
92
if err != nil {
98
- return fmt.Errorf("Handshake3 could not decode remote msg: %q", err)
93
+ return nil, fmt.Errorf("Handshake3 could not decode remote msg: %q", err)
94
}
95
96
log.Debugf("Handshake3 received from %s", rpeer)
97
}
98
104
- if err := handshake.Handshake3UpdatePeer(rpeer, remoteH); err != nil {
105
- log.Errorf("Handshake3 failed to update %s", rpeer)
106
- return err
107
- }
108
-
109
- // If we are behind a NAT, inform the user that certain things might not work yet
110
- nat, err := checkNAT(remoteH.GetObservedAddr())
111
- if err != nil {
112
- log.Errorf("Error in NAT detection: %s", err)
113
- }
114
- if nat {
115
- msg := `Remote peer observed our address to be: %s
116
- The local addresses are: %s
117
- Thus, connection is going through NAT, and other connections may fail.
118
-
119
- IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
120
- Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
121
- `
122
- addrs, _ := u.GetLocalAddresses()
123
- log.Warning(fmt.Sprintf(msg, remoteH.GetObservedAddr(), addrs))
124
- }
125
-
126
- return nil
127
-}
128
-
129
-// checkNAT returns whether or not we might be behind a NAT
130
-func checkNAT(observedaddr string) (bool, error) {
131
- observedma, err := ma.NewMultiaddr(observedaddr)
132
- if err != nil {
133
- return false, err
134
- }
135
- addrs, err := u.GetLocalAddresses()
99
+ // actually update our state based on the new knowledge
100
+ res, err := handshake.Handshake3Update(lpeer, rpeer, remoteH)
101
if err != nil {
137
- return false, err
138
- }
139
-
140
- omastr := observedma.String()
141
- for _, addr := range addrs {
142
- if strings.HasPrefix(omastr, addr.String()) {
143
- return false, nil
144
- }
102
+ log.Errorf("Handshake3 failed to update %s", rpeer)
103
}
146
-
147
- return true, nil
104
+ res.RemoteObservedAddress = c.RemoteMultiaddr()
105
+ return res, nil
106
}
net/handshake/handshake3.go
+36
-10
@@ -13,18 +13,21 @@ import (
13
var log = u.Logger("handshake")
14
15
// Handshake3Msg constructs a Handshake3 msg.
16
-func Handshake3Msg(localPeer peer.Peer) *pb.Handshake3 {
16
+func Handshake3Msg(localPeer peer.Peer, remoteAddr ma.Multiaddr) *pb.Handshake3 {
17
var msg pb.Handshake3
18
// don't need publicKey after secure channel.
19
// msg.PublicKey = localPeer.PubKey().Bytes()
20
21
- // addresses
21
+ // local listen addresses
22
addrs := localPeer.Addresses()
23
msg.ListenAddrs = make([][]byte, len(addrs))
24
for i, a := range addrs {
25
msg.ListenAddrs[i] = a.Bytes()
26
}
27
28
+ // observed remote address
29
+ msg.ObservedAddr = remoteAddr.Bytes()
30
+
31
// services
32
// srv := localPeer.Services()
33
// msg.Services = make([]mux.ProtocolID, len(srv))
@@ -35,20 +38,43 @@ func Handshake3Msg(localPeer peer.Peer) *pb.Handshake3 {
38
return &msg
39
}
40
38
-// Handshake3UpdatePeer updates a remote peer with the information in the
39
-// handshake3 msg we received from them.
40
-func Handshake3UpdatePeer(remotePeer peer.Peer, msg *pb.Handshake3) error {
41
+// Handshake3Update updates local knowledge with the information in the
42
+// handshake3 msg we received from remote client.
43
+func Handshake3Update(lpeer, rpeer peer.Peer, msg *pb.Handshake3) (*Handshake3Result, error) {
44
+ res := &Handshake3Result{}
45
+
46
+ // our observed address
47
+ observedAddr, err := ma.NewMultiaddrBytes(msg.GetObservedAddr())
48
+ if err != nil {
49
+ return res, err
50
+ }
51
+ lpeer.AddAddress(observedAddr)
52
+ res.LocalObservedAddress = observedAddr
53
42
- // addresses
54
+ // remote's reported addresses
55
for _, a := range msg.GetListenAddrs() {
56
addr, err := ma.NewMultiaddrBytes(a)
57
if err != nil {
58
err = fmt.Errorf("remote peer address not a multiaddr: %s", err)
47
- log.Errorf("Handshake3: error %s", err)
48
- return err
59
+ log.Errorf("Handshake3 error %s", err)
60
+ return res, err
61
}
50
- remotePeer.AddAddress(addr)
62
+ rpeer.AddAddress(addr)
63
+ res.RemoteListenAddresses = append(res.RemoteListenAddresses, addr)
64
}
65
53
- return nil
66
+ return res, nil
67
+}
68
+
69
+// Handshake3Result collects the knowledge gained in Handshake3.
70
+type Handshake3Result struct {
71
+
72
+ // The addresses reported by the remote client
73
+ RemoteListenAddresses []ma.Multiaddr
74
+
75
+ // The address of the remote client we observed in this connection
76
+ RemoteObservedAddress ma.Multiaddr
77
+
78
+ // The address the remote client observed from this connection
79
+ LocalObservedAddress ma.Multiaddr
80
}
net/handshake/pb/handshake.pb.go
+13
-9
@@ -15,10 +15,12 @@ It has these top-level messages:
15
package handshake_pb
16
17
import proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/proto"
18
+import json "encoding/json"
19
import math "math"
20
20
-// Reference imports to suppress errors if they are not otherwise used.
21
+// Reference proto, json, and math imports to suppress error if they are not otherwise used.
22
var _ = proto.Marshal
23
+var _ = &json.SyntaxError{}
24
var _ = math.Inf
25
26
// Handshake1 is delivered _before_ the secure channel is initialized
@@ -51,11 +53,13 @@ func (m *Handshake1) GetAgentVersion() string {
53
54
// Handshake3 is delivered _after_ the secure channel is initialized
55
type Handshake3 struct {
54
- // listenAddrs are the multiaddrs this node listens for open connections on
56
+ // listenAddrs are the multiaddrs the sender node listens for open connections on
57
ListenAddrs [][]byte `protobuf:"bytes,2,rep,name=listenAddrs" json:"listenAddrs,omitempty"`
56
- // we'll have more fields here later.
57
- ObservedAddr *string `protobuf:"bytes,4,opt,name=observedAddr" json:"observedAddr,omitempty"`
58
- XXX_unrecognized []byte `json:"-"`
58
+ // oservedAddr is the multiaddr of the remote endpoint that the sender node perceives
59
+ // this is useful information to convey to the other side, as it helps the remote endpoint
60
+ // determine whether its connection to the local peer goes through NAT.
61
+ ObservedAddr []byte `protobuf:"bytes,4,opt,name=observedAddr" json:"observedAddr,omitempty"`
62
+ XXX_unrecognized []byte `json:"-"`
63
}
64
65
func (m *Handshake3) Reset() { *m = Handshake3{} }
@@ -69,11 +73,11 @@ func (m *Handshake3) GetListenAddrs() [][]byte {
73
return nil
74
}
75
72
-func (m *Handshake3) GetObservedAddr() string {
73
- if m != nil && m.ObservedAddr != nil {
74
- return *m.ObservedAddr
76
+func (m *Handshake3) GetObservedAddr() []byte {
77
+ if m != nil {
78
+ return m.ObservedAddr
79
}
76
- return ""
80
+ return nil
81
}
82
83
func init() {
net/handshake/pb/handshake.proto
+3
-3
@@ -22,7 +22,7 @@ message Handshake3 {
22
// - then again, if we change / disable secure channel, may still want it.
23
// optional bytes publicKey = 1;
24
25
- // listenAddrs are the multiaddrs this node listens for open connections on
25
+ // listenAddrs are the multiaddrs the sender node listens for open connections on
26
repeated bytes listenAddrs = 2;
27
28
// TODO
@@ -31,8 +31,8 @@ message Handshake3 {
31
32
// we'll have more fields here later.
33
34
- // oservedAddr is the multiaddr of the remote endpoint that the local node perceives
34
+ // oservedAddr is the multiaddr of the remote endpoint that the sender node perceives
35
// this is useful information to convey to the other side, as it helps the remote endpoint
36
// determine whether its connection to the local peer goes through NAT.
37
- optional string observedAddr = 4;
37
+ optional bytes observedAddr = 4;
38
}
net/swarm/addrs.go
+33
@@ -72,3 +72,36 @@ func interfaceAddresses() ([]ma.Multiaddr, error) {
72
73
return nonLoopback, nil
74
}
75
+
76
+// addrInList returns whether or not an address is part of a list.
77
+// this is useful to check if NAT is happening (or other bugs?)
78
+func addrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
79
+ for _, addr2 := range list {
80
+ if addr.Equal(addr2) {
81
+ return true
82
+ }
83
+ }
84
+ return false
85
+}
86
+
87
+// checkNATWarning checks if our observed addresses differ. if so,
88
+// informs the user that certain things might not work yet
89
+func (s *Swarm) checkNATWarning(observed ma.Multiaddr) {
90
+ listen, err := s.InterfaceListenAddresses()
91
+ if err != nil {
92
+ log.Errorf("Error retrieving swarm.InterfaceListenAddresses: %s", err)
93
+ return
94
+ }
95
+
96
+ if !addrInList(observed, listen) { // probably a nat
97
+ log.Warningf(natWarning, observed, listen)
98
+ }
99
+}
100
+
101
+const natWarning = `Remote peer observed our address to be: %s
102
+The local addresses are: %s
103
+Thus, connection is going through NAT, and other connections may fail.
104
+
105
+IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
106
+Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
107
+`
net/swarm/conn.go
+5
-1
@@ -112,11 +112,15 @@ func (s *Swarm) connSetup(c conn.Conn) (conn.Conn, error) {
112
113
// handshake3
114
ctxT, _ := context.WithTimeout(c.Context(), conn.HandshakeTimeout)
115
- if err := conn.Handshake3(ctxT, c); err != nil {
115
+ h3result, err := conn.Handshake3(ctxT, c)
116
+ if err != nil {
117
c.Close()
118
return nil, fmt.Errorf("Handshake3 failed: %s", err)
119
}
120
121
+ // check for nats. you know, just in case.
122
+ s.checkNATWarning(h3result.LocalObservedAddress)
123
+
124
// add to conns
125
s.connsLock.Lock()
126
util/util.go
+1
-58
@@ -4,16 +4,13 @@ import (
4
"errors"
5
"io"
6
"math/rand"
7
- "net"
7
"os"
8
"path/filepath"
10
- "reflect"
9
"strings"
10
"time"
11
12
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
15
- ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
16
- manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
13
+
14
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/mitchellh/go-homedir"
15
)
16
@@ -111,57 +108,3 @@ func GetenvBool(name string) bool {
108
v := strings.ToLower(os.Getenv(name))
109
return v == "true" || v == "t" || v == "1"
110
}
114
-
115
-// IsLoopbackAddr returns whether or not the ip portion of the passed in multiaddr
116
-// string is a loopback address
117
-func IsLoopbackAddr(addr string) bool {
118
- loops := []string{"/ip4/127.0.0.1", "/ip6/::1"}
119
- for _, loop := range loops {
120
- if strings.HasPrefix(addr, loop) {
121
- return true
122
- }
123
- }
124
- return false
125
-}
126
-
127
-// GetLocalAddresses returns a list of ip addresses associated with
128
-// the local machine
129
-func GetLocalAddresses() ([]ma.Multiaddr, error) {
130
- // Enumerate interfaces on this machine
131
- ifaces, err := net.Interfaces()
132
- if err != nil {
133
- return nil, err
134
- }
135
-
136
- var maddrs []ma.Multiaddr
137
- for _, i := range ifaces {
138
- addrs, err := i.Addrs()
139
- if err != nil {
140
- log.Warningf("Skipping addr: %s", err)
141
- continue
142
- }
143
- // Check each address and convert to a multiaddr
144
- for _, addr := range addrs {
145
- switch v := addr.(type) {
146
- case *net.IPNet:
147
-
148
- // Build multiaddr
149
- maddr, err := manet.FromIP(v.IP)
150
- if err != nil {
151
- log.Errorf("maddr parsing error: %s", err)
152
- continue
153
- }
154
-
155
- // Dont list loopback addresses
156
- if IsLoopbackAddr(maddr.String()) {
157
- continue
158
- }
159
- maddrs = append(maddrs, maddr)
160
- default:
161
- // Not sure if any other types will show up here
162
- log.Errorf("Got '%s' type = '%s'", v, reflect.TypeOf(v))
163
- }
164
- }
165
- }
166
- return maddrs, nil
167
-}