Skip to content

Commit

Permalink
hole punching
Browse files Browse the repository at this point in the history
  • Loading branch information
lnykww committed Mar 17, 2019
1 parent 5da711d commit 7740e9b
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 2 deletions.
6 changes: 6 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
"github.com/libp2p/go-libp2p/p2p/host/punch"
relay "github.com/libp2p/go-libp2p/p2p/host/relay"
routed "github.com/libp2p/go-libp2p/p2p/host/routed"

Expand Down Expand Up @@ -68,6 +69,7 @@ type Config struct {
Routing RoutingC

EnableAutoRelay bool
EnablePunch bool
}

// NewNode constructs a new libp2p Host from the Config.
Expand Down Expand Up @@ -168,6 +170,10 @@ func (cfg *Config) NewNode(ctx context.Context) (host.Host, error) {
return nil, err
}

if cfg.EnablePunch {
_ = punch.NewPunch(h.(*bhost.BasicHost))
}

// Configure routing and autorelay
var router routing.PeerRouting
if cfg.Routing != nil {
Expand Down
8 changes: 8 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ func EnableAutoRelay() Option {
}
}

// EnablePunch configures libp2p to enbale hole punch
func EnablePunch() Option {
return func(cfg *Config) error {
cfg.EnablePunch = true
return nil
}
}

// FilterAddresses configures libp2p to never dial nor accept connections from
// the given addresses.
func FilterAddresses(addrs ...*net.IPNet) Option {
Expand Down
7 changes: 7 additions & 0 deletions p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
logging "github.com/ipfs/go-log"
goprocess "github.com/jbenet/goprocess"
goprocessctx "github.com/jbenet/goprocess/context"
autonat "github.com/libp2p/go-libp2p-autonat"
ifconnmgr "github.com/libp2p/go-libp2p-interface-connmgr"
inat "github.com/libp2p/go-libp2p-nat"
inet "github.com/libp2p/go-libp2p-net"
Expand Down Expand Up @@ -162,6 +163,8 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
h.pings = ping.NewPingService(h)
}

h.autonat = autonat.NewAutoNAT(ctx, h, h.Addrs)

net.SetConnHandler(h.newConnHandler)
net.SetStreamHandler(h.newStreamHandler)
return h, nil
Expand Down Expand Up @@ -260,6 +263,10 @@ func (h *BasicHost) newStreamHandler(s inet.Stream) {
go handle(protoID, s)
}

func (h *BasicHost) AutoNat() autonat.AutoNAT {
return h.autonat
}

// 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() {
Expand Down
93 changes: 93 additions & 0 deletions p2p/host/punch/pb/punch.pb.go

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

9 changes: 9 additions & 0 deletions p2p/host/punch/pb/punch.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
syntax = "proto2";

package punch.pb;

message Punch {
optional string version=1;
repeated string addresses=2;
optional int32 natStatus=3;
}
142 changes: 142 additions & 0 deletions p2p/host/punch/punch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package punch

import (
"context"
"time"

ggio "github.com/gogo/protobuf/io"
autonat "github.com/libp2p/go-libp2p-autonat"
circuit "github.com/libp2p/go-libp2p-circuit"
inet "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer"
swarm "github.com/libp2p/go-libp2p-swarm"
basic "github.com/libp2p/go-libp2p/p2p/host/basic"
pb "github.com/libp2p/go-libp2p/p2p/host/punch/pb"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr-net"
)

type Punch struct {
*basic.BasicHost
}

const PunchProtocol = "/p2p/punch/1.0.0"
const PunchRetry = 5

func NewPunch(bhost *basic.BasicHost) *Punch {
p := &Punch{
bhost,
}
bhost.SetStreamHandler(PunchProtocol, p.PunchInfoHandler)
bhost.Network().Notify(p)
return p
}

func (p *Punch) GetPunchInfo(peerID peer.ID) ([]ma.Multiaddr, autonat.NATStatus, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
s, err := p.NewStream(ctx, peerID, PunchProtocol)
if err != nil {
return nil, autonat.NATStatusUnknown, err
}

defer inet.FullClose(s)

r := ggio.NewDelimitedReader(s, 2048)
mes := pb.Punch{}
if err := r.ReadMsg(&mes); err != nil {
s.Reset()
return nil, autonat.NATStatusUnknown, err
}

multiaddrs := make([]ma.Multiaddr, 0)

for _, mas := range mes.Addresses {
ma, err := ma.NewMultiaddr(mas)
if err != nil {
continue
}

multiaddrs = append(multiaddrs, ma)
}

return multiaddrs, autonat.NATStatus(*mes.NatStatus), nil
}

func (p *Punch) PunchInfoHandler(s inet.Stream) {
defer inet.FullClose(s)
natstatus := int32(p.AutoNat().Status())

w := ggio.NewDelimitedWriter(s)
mes := pb.Punch{
Addresses: make([]string, 0),
NatStatus: &natstatus,
}
for _, addr := range p.AllAddrs() {
if manet.IsPrivateAddr(addr) {
continue
}

if _, err := addr.ValueForProtocol(circuit.P_CIRCUIT); err == nil {
continue
}
if _, err := addr.ValueForProtocol(ma.P_QUIC); err != nil {
continue
}

mes.Addresses = append(mes.Addresses, addr.String())
}
w.WriteMsg(&mes)
}

func (p *Punch) Listen(inet.Network, ma.Multiaddr) {}
func (p *Punch) ListenClose(inet.Network, ma.Multiaddr) {}
func (p *Punch) Connected(_ inet.Network, c inet.Conn) {
// Break if we are public, because we don't need any action
// with hole punching, just let peer connect us.
if p.AutoNat().Status() == autonat.NATStatusPublic {
return
}
// Forget the direct connection.
if _, err := c.RemoteMultiaddr().ValueForProtocol(circuit.P_CIRCUIT); err != nil {
return
}
peerID := c.RemotePeer()
go func() {
addrs, _, err := p.GetPunchInfo(peerID)
if err != nil || len(addrs) == 0 {
return
}
punch := func() {
var s *swarm.Swarm
var ok bool
s, ok = p.Network().(*swarm.Swarm)
if !ok {
return
}
s.Backoff().Clear(peerID)

//peerInfo := pstore.PeerInfo{
// ID: peerID,
// Addrs: addrs,
//}
for i := 0; i < PunchRetry; i++ {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err := s.DialDirect(ctx, peerID, addrs)
if err != nil {
continue
}
// TODO: stream migrate
c.Close()
return
}
}
punch()
}()
}

func (p *Punch) Disconnected(_ inet.Network, c inet.Conn) {}

func (p *Punch) OpenedStream(inet.Network, inet.Stream) {}
func (p *Punch) ClosedStream(inet.Network, inet.Stream) {}
4 changes: 2 additions & 2 deletions p2p/host/relay/autorelay.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func NewAutoRelayHost(ctx context.Context, bhost *basic.BasicHost, discover disc
func (h *AutoRelayHost) hostAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
h.mx.Lock()
defer h.mx.Unlock()
if h.addrs != nil && h.autonat.Status() == autonat.NATStatusPrivate {
if h.addrs != nil && h.AutoNat().Status() == autonat.NATStatusPrivate {
return h.addrs
} else {
return filterUnspecificRelay(h.addrsF(addrs))
Expand All @@ -95,7 +95,7 @@ func (h *AutoRelayHost) background(ctx context.Context) {

for {
wait := autonat.AutoNATRefreshInterval
switch h.autonat.Status() {
switch h.AutoNat().Status() {
case autonat.NATStatusUnknown:
wait = autonat.AutoNATRetryInterval
case autonat.NATStatusPublic:
Expand Down

0 comments on commit 7740e9b

Please sign in to comment.