Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] UDP-based NAT hole-punching #490

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
protocol "github.com/libp2p/go-libp2p-protocol"
identify "github.com/libp2p/go-libp2p/p2p/protocol/identify"
ping "github.com/libp2p/go-libp2p/p2p/protocol/ping"
punching "github.com/libp2p/go-libp2p/p2p/protocol/punching"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
msmux "github.com/multiformats/go-multistream"
Expand Down Expand Up @@ -56,7 +57,7 @@ const NATPortMap Option = iota
type BasicHost struct {
network inet.Network
mux *msmux.MultistreamMuxer
ids *identify.IDService
Ids *identify.IDService
pings *ping.PingService
natmgr NATManager
maResolver *madns.Resolver
Expand Down Expand Up @@ -126,10 +127,10 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
}

if opts.IdentifyService != nil {
h.ids = opts.IdentifyService
h.Ids = opts.IdentifyService
} else {
// we can't set this as a default above because it depends on the *BasicHost.
h.ids = identify.NewIDService(h)
h.Ids = identify.NewIDService(h)
}

if uint64(opts.NegotiationTimeout) != 0 {
Expand Down Expand Up @@ -159,6 +160,9 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
h.pings = ping.NewPingService(h)
}

// FIXME move out of basic_host
punching.NewPunchingService(h, h.Ids)

net.SetConnHandler(h.newConnHandler)
net.SetStreamHandler(h.newStreamHandler)
return h, nil
Expand Down Expand Up @@ -206,7 +210,7 @@ func (h *BasicHost) newConnHandler(c inet.Conn) {
// Clear protocols on connecting to new peer to avoid issues caused
// by misremembering protocols between reconnects
h.Peerstore().SetProtocols(c.RemotePeer())
h.ids.IdentifyConn(c)
h.Ids.IdentifyConn(c)
}

// newStreamHandler is the remote-opened stream handler for inet.Network
Expand Down Expand Up @@ -260,7 +264,7 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) {
// PushIdentify pushes an identify update through the identify push protocol
// Warning: this interface is unstable and may disappear in the future.
func (h *BasicHost) PushIdentify() {
h.ids.Push()
h.Ids.Push()
}

// ID returns the (local) peer.ID associated with this Host
Expand All @@ -285,7 +289,7 @@ func (h *BasicHost) Mux() *msmux.MultistreamMuxer {

// IDService returns
func (h *BasicHost) IDService() *identify.IDService {
return h.ids
return h.Ids
}

// SetStreamHandler sets the protocol handler on the Host's Mux.
Expand Down Expand Up @@ -459,7 +463,7 @@ func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error {
// identify the connection before returning.
done := make(chan struct{})
go func() {
h.ids.IdentifyConn(c)
h.Ids.IdentifyConn(c)
close(done)
}()

Expand Down Expand Up @@ -508,9 +512,9 @@ func (h *BasicHost) AllAddrs() []ma.Multiaddr {
log.Debug("error retrieving network interface addrs")
}
var observedAddrs []ma.Multiaddr
if h.ids != nil {
if h.Ids != nil {
// peer observed addresses
observedAddrs = h.ids.OwnObservedAddrs()
observedAddrs = h.Ids.OwnObservedAddrs()
}
var natAddrs []ma.Multiaddr
// natmgr is nil if we do not use nat option;
Expand Down
4 changes: 2 additions & 2 deletions p2p/host/basic/basic_host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,13 @@ func TestHostProtoPreknowledge(t *testing.T) {

// wait for identify handshake to finish completely
select {
case <-h1.ids.IdentifyWait(h1.Network().ConnsToPeer(h2.ID())[0]):
case <-h1.Ids.IdentifyWait(h1.Network().ConnsToPeer(h2.ID())[0]):
case <-time.After(time.Second * 5):
t.Fatal("timed out waiting for identify")
}

select {
case <-h2.ids.IdentifyWait(h2.Network().ConnsToPeer(h1.ID())[0]):
case <-h2.Ids.IdentifyWait(h2.Network().ConnsToPeer(h1.ID())[0]):
case <-time.After(time.Second * 5):
t.Fatal("timed out waiting for identify")
}
Expand Down
4 changes: 3 additions & 1 deletion p2p/host/relay/autorelay.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ func (h *AutoRelayHost) background(ctx context.Context) {

for {
wait := autonat.AutoNATRefreshInterval
switch h.autonat.Status() {
status := h.autonat.Status()
h.Ids.SetNatStatus(status)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's gross -- we should embed AutoNAT in basic_host if identify needs it or pass it a reference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is not just gross, it's also wrong -- identify should poll the NATStatus directly instead of a second-hand delayed version.

switch status {
case autonat.NATStatusUnknown:
wait = autonat.AutoNATRetryInterval
case autonat.NATStatusPublic:
Expand Down
40 changes: 38 additions & 2 deletions p2p/protocol/identify/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ package identify

import (
"context"
"net"
"sync"
"time"

pb "github.com/libp2p/go-libp2p/p2p/protocol/identify/pb"

ggio "github.com/gogo/protobuf/io"
logging "github.com/ipfs/go-log"
autonat "github.com/libp2p/go-libp2p-autonat"
ic "github.com/libp2p/go-libp2p-crypto"
host "github.com/libp2p/go-libp2p-host"
lgbl "github.com/libp2p/go-libp2p-loggables"
inet "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer"
pstore "github.com/libp2p/go-libp2p-peerstore"
pb "github.com/libp2p/go-libp2p/p2p/protocol/identify/pb"
ma "github.com/multiformats/go-multiaddr"
msmux "github.com/multiformats/go-multistream"
)
Expand Down Expand Up @@ -54,6 +55,9 @@ type IDService struct {
// our own observed addresses.
// TODO: instead of expiring, remove these when we disconnect
observedAddrs ObservedAddrSet

// NAT status
natStatus pb.Identify_NATStatus
}

// NewIDService constructs a new *IDService and activates it by
Expand Down Expand Up @@ -163,6 +167,21 @@ func (ids *IDService) Push() {
}
}

func (ids *IDService) SetNatStatus(status autonat.NATStatus) {
switch status {
case autonat.NATStatusPrivate:
ids.natStatus = pb.Identify_NATStatusPrivate
case autonat.NATStatusPublic:
ids.natStatus = pb.Identify_NATStatusPublic
default:
ids.natStatus = pb.Identify_NATStatusUnknown
}
}

func (ids *IDService) GetNatStatus() pb.Identify_NATStatus {
return ids.natStatus
}

func (ids *IDService) populateMessage(mes *pb.Identify, c inet.Conn) {

// set protocols this node is currently handling
Expand Down Expand Up @@ -201,6 +220,9 @@ func (ids *IDService) populateMessage(mes *pb.Identify, c inet.Conn) {
av := ClientVersion
mes.ProtocolVersion = &pv
mes.AgentVersion = &av

// set if behind NAT when possible
mes.NatStatus = &ids.natStatus
}

func (ids *IDService) consumeMessage(mes *pb.Identify, c inet.Conn) {
Expand All @@ -209,6 +231,8 @@ func (ids *IDService) consumeMessage(mes *pb.Identify, c inet.Conn) {
// mes.Protocols
ids.Host.Peerstore().SetProtocols(p, mes.Protocols...)

ids.Host.Peerstore().Put(p, "natStatus", mes.GetNatStatus())

// mes.ObservedAddr
ids.consumeObservedAddress(mes.GetObservedAddr(), c)

Expand Down Expand Up @@ -412,6 +436,18 @@ func (ids *IDService) consumeObservedAddress(observed []byte, c inet.Conn) {
}

func addrInAddrs(a ma.Multiaddr, as []ma.Multiaddr) bool {
// allow wildcard addresses
if ip, err := a.ValueForProtocol(ma.P_IP4); err == nil {
if parsed := net.ParseIP(ip); parsed != nil && parsed.IsUnspecified() {
return true
}
}
if ip, err := a.ValueForProtocol(ma.P_IP6); err == nil {
if parsed := net.ParseIP(ip); parsed != nil && parsed.IsUnspecified() {
return true
}
}

for _, b := range as {
if a.Equal(b) {
return true
Expand Down
124 changes: 104 additions & 20 deletions p2p/protocol/identify/pb/identify.pb.go

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

Loading