Skip to content

Commit

Permalink
Merge pull request #253 from jbenet/net-detect
Browse files Browse the repository at this point in the history
NAT detect
  • Loading branch information
jbenet committed Nov 4, 2014
2 parents 60ef8e5 + 3e62042 commit d303ff4
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 9 deletions.
45 changes: 45 additions & 0 deletions net/conn/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package conn
import (
"errors"
"fmt"
"strings"

handshake "github.com/jbenet/go-ipfs/net/handshake"
hspb "github.com/jbenet/go-ipfs/net/handshake/pb"
u "github.com/jbenet/go-ipfs/util"

context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
ma "github.com/jbenet/go-multiaddr"
)

// Handshake1 exchanges local and remote versions and compares them
Expand Down Expand Up @@ -65,6 +68,10 @@ func Handshake3(ctx context.Context, c Conn) error {

var remoteH, localH *hspb.Handshake3
localH = handshake.Handshake3Msg(lpeer)

rma := c.RemoteMultiaddr()
localH.ObservedAddr = proto.String(rma.String())

localB, err := proto.Marshal(localH)
if err != nil {
return err
Expand Down Expand Up @@ -99,5 +106,43 @@ func Handshake3(ctx context.Context, c Conn) error {
return err
}

// If we are behind a NAT, inform the user that certain things might not work yet
nat, err := checkNAT(remoteH.GetObservedAddr())
if err != nil {
log.Errorf("Error in NAT detection: %s", err)
}
if nat {
msg := `Remote peer observed our address to be: %s
The local addresses are: %s
Thus, connection is going through NAT, and other connections may fail.
IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
`
addrs, _ := u.GetLocalAddresses()
log.Warning(fmt.Sprintf(msg, remoteH.GetObservedAddr(), addrs))
}

return nil
}

// checkNAT returns whether or not we might be behind a NAT
func checkNAT(observedaddr string) (bool, error) {
observedma, err := ma.NewMultiaddr(observedaddr)
if err != nil {
return false, err
}
addrs, err := u.GetLocalAddresses()
if err != nil {
return false, err
}

omastr := observedma.String()
for _, addr := range addrs {
if strings.HasPrefix(omastr, addr.String()) {
return false, nil
}
}

return true, nil
}
21 changes: 13 additions & 8 deletions net/handshake/pb/handshake.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion net/handshake/pb/handshake.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package handshake.pb;

import "github.com/jbenet/go-ipfs/net/mux/mux.proto";
//import "github.com/jbenet/go-ipfs/net/mux/mux.proto";

// Handshake1 is delivered _before_ the secure channel is initialized
message Handshake1 {
Expand Down Expand Up @@ -30,4 +30,9 @@ message Handshake3 {
// repeated mux.ProtocolID services = 3;

// we'll have more fields here later.

// oservedAddr is the multiaddr of the remote endpoint that the local node perceives
// this is useful information to convey to the other side, as it helps the remote endpoint
// determine whether its connection to the local peer goes through NAT.
optional string observedAddr = 4;
}
11 changes: 11 additions & 0 deletions net/swarm/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package swarm
import (
"errors"
"fmt"
"strings"
"sync"

conn "github.com/jbenet/go-ipfs/net/conn"
Expand Down Expand Up @@ -127,6 +128,16 @@ func (s *Swarm) Dial(peer peer.Peer) (conn.Conn, error) {
Peerstore: s.peers,
}

// If we are attempting to connect to the zero addr, fail out early
raddr := peer.NetAddress("tcp")
if raddr == nil {
return nil, fmt.Errorf("No remote address for network tcp")
}

if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
return nil, fmt.Errorf("Attempted to connect to loopback address: %s", raddr)
}

c, err = d.Dial(s.Context(), "tcp", peer)
if err != nil {
return nil, err
Expand Down
58 changes: 58 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"errors"
"io"
"math/rand"
"net"
"os"
"path/filepath"
"reflect"
"strings"
"time"

ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
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"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/mitchellh/go-homedir"
)

Expand Down Expand Up @@ -107,3 +111,57 @@ func GetenvBool(name string) bool {
v := strings.ToLower(os.Getenv(name))
return v == "true" || v == "t" || v == "1"
}

// IsLoopbackAddr returns whether or not the ip portion of the passed in multiaddr
// string is a loopback address
func IsLoopbackAddr(addr string) bool {
loops := []string{"/ip4/127.0.0.1", "/ip6/::1"}
for _, loop := range loops {
if strings.HasPrefix(addr, loop) {
return true
}
}
return false
}

// GetLocalAddresses returns a list of ip addresses associated with
// the local machine
func GetLocalAddresses() ([]ma.Multiaddr, error) {
// Enumerate interfaces on this machine
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}

var maddrs []ma.Multiaddr
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
log.Warningf("Skipping addr: %s", err)
continue
}
// Check each address and convert to a multiaddr
for _, addr := range addrs {
switch v := addr.(type) {
case *net.IPNet:

// Build multiaddr
maddr, err := manet.FromIP(v.IP)
if err != nil {
log.Errorf("maddr parsing error: %s", err)
continue
}

// Dont list loopback addresses
if IsLoopbackAddr(maddr.String()) {
continue
}
maddrs = append(maddrs, maddr)
default:
// Not sure if any other types will show up here
log.Errorf("Got '%s' type = '%s'", v, reflect.TypeOf(v))
}
}
}
return maddrs, nil
}

0 comments on commit d303ff4

Please sign in to comment.