Skip to content

Commit 4989dca

Browse files
committed
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)
1 parent 0135e3e commit 4989dca

File tree

7 files changed

+105
-137
lines changed

7 files changed

+105
-137
lines changed

net/conn/handshake.go

+14-56
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@ package conn
33
import (
44
"errors"
55
"fmt"
6-
"strings"
76

87
handshake "github.com/jbenet/go-ipfs/net/handshake"
98
hspb "github.com/jbenet/go-ipfs/net/handshake/pb"
10-
u "github.com/jbenet/go-ipfs/util"
119

1210
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
1311
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"
1512
)
1613

1714
// Handshake1 exchanges local and remote versions and compares them
@@ -62,87 +59,48 @@ func Handshake1(ctx context.Context, c Conn) error {
6259
}
6360

6461
// 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) {
6663
rpeer := c.RemotePeer()
6764
lpeer := c.LocalPeer()
6865

66+
// setup + send the message to remote
6967
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())
7569
localB, err := proto.Marshal(localH)
7670
if err != nil {
77-
return err
71+
return nil, err
7872
}
7973

8074
c.Out() <- localB
8175
log.Debugf("Handshake1: sent to %s", rpeer)
8276

77+
// wait + listen for response
8378
select {
8479
case <-ctx.Done():
85-
return ctx.Err()
80+
return nil, ctx.Err()
8681

8782
case <-c.Closing():
88-
return errors.New("Handshake3: error remote connection closed")
83+
return nil, errors.New("Handshake3: error remote connection closed")
8984

9085
case remoteB, ok := <-c.In():
9186
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)
9388
}
9489

9590
remoteH = new(hspb.Handshake3)
9691
err = proto.Unmarshal(remoteB, remoteH)
9792
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)
9994
}
10095

10196
log.Debugf("Handshake3 received from %s", rpeer)
10297
}
10398

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)
136101
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)
145103
}
146-
147-
return true, nil
104+
res.RemoteObservedAddress = c.RemoteMultiaddr()
105+
return res, nil
148106
}

net/handshake/handshake3.go

+36-10
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,21 @@ import (
1313
var log = u.Logger("handshake")
1414

1515
// Handshake3Msg constructs a Handshake3 msg.
16-
func Handshake3Msg(localPeer peer.Peer) *pb.Handshake3 {
16+
func Handshake3Msg(localPeer peer.Peer, remoteAddr ma.Multiaddr) *pb.Handshake3 {
1717
var msg pb.Handshake3
1818
// don't need publicKey after secure channel.
1919
// msg.PublicKey = localPeer.PubKey().Bytes()
2020

21-
// addresses
21+
// local listen addresses
2222
addrs := localPeer.Addresses()
2323
msg.ListenAddrs = make([][]byte, len(addrs))
2424
for i, a := range addrs {
2525
msg.ListenAddrs[i] = a.Bytes()
2626
}
2727

28+
// observed remote address
29+
msg.ObservedAddr = remoteAddr.Bytes()
30+
2831
// services
2932
// srv := localPeer.Services()
3033
// msg.Services = make([]mux.ProtocolID, len(srv))
@@ -35,20 +38,43 @@ func Handshake3Msg(localPeer peer.Peer) *pb.Handshake3 {
3538
return &msg
3639
}
3740

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
4153

42-
// addresses
54+
// remote's reported addresses
4355
for _, a := range msg.GetListenAddrs() {
4456
addr, err := ma.NewMultiaddrBytes(a)
4557
if err != nil {
4658
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
4961
}
50-
remotePeer.AddAddress(addr)
62+
rpeer.AddAddress(addr)
63+
res.RemoteListenAddresses = append(res.RemoteListenAddresses, addr)
5164
}
5265

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

net/handshake/pb/handshake.pb.go

+13-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

net/handshake/pb/handshake.proto

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ message Handshake3 {
2222
// - then again, if we change / disable secure channel, may still want it.
2323
// optional bytes publicKey = 1;
2424

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
2626
repeated bytes listenAddrs = 2;
2727

2828
// TODO
@@ -31,8 +31,8 @@ message Handshake3 {
3131

3232
// we'll have more fields here later.
3333

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
3535
// this is useful information to convey to the other side, as it helps the remote endpoint
3636
// determine whether its connection to the local peer goes through NAT.
37-
optional string observedAddr = 4;
37+
optional bytes observedAddr = 4;
3838
}

net/swarm/addrs.go

+33
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,36 @@ func interfaceAddresses() ([]ma.Multiaddr, error) {
7272

7373
return nonLoopback, nil
7474
}
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
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,15 @@ func (s *Swarm) connSetup(c conn.Conn) (conn.Conn, error) {
112112

113113
// handshake3
114114
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 {
116117
c.Close()
117118
return nil, fmt.Errorf("Handshake3 failed: %s", err)
118119
}
119120

121+
// check for nats. you know, just in case.
122+
s.checkNATWarning(h3result.LocalObservedAddress)
123+
120124
// add to conns
121125
s.connsLock.Lock()
122126

util/util.go

+1-58
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@ import (
44
"errors"
55
"io"
66
"math/rand"
7-
"net"
87
"os"
98
"path/filepath"
10-
"reflect"
119
"strings"
1210
"time"
1311

1412
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+
1714
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/mitchellh/go-homedir"
1815
)
1916

@@ -111,57 +108,3 @@ func GetenvBool(name string) bool {
111108
v := strings.ToLower(os.Getenv(name))
112109
return v == "true" || v == "t" || v == "1"
113110
}
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-
}

0 commit comments

Comments
 (0)