-
Notifications
You must be signed in to change notification settings - Fork 37
Support for Hole punching #233
Changes from 8 commits
7d13adb
59a5292
db3134d
979b039
fca9031
bd88bb2
3c21711
b55ba31
f433114
56ecb0f
8b8bc9a
874c817
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -334,6 +334,7 @@ func (s *Swarm) NewStream(ctx context.Context, p peer.ID) (network.Stream, error | |
// a non-closed connection. | ||
dials := 0 | ||
for { | ||
// will prefer direct connections over relayed connections for opening streams | ||
c := s.bestConnToPeer(p) | ||
if c == nil { | ||
if nodial, _ := network.GetNoDial(ctx); nodial { | ||
|
@@ -378,15 +379,17 @@ func (s *Swarm) ConnsToPeer(p peer.ID) []network.Conn { | |
|
||
// bestConnToPeer returns the best connection to peer. | ||
func (s *Swarm) bestConnToPeer(p peer.ID) *Conn { | ||
// Selects the best connection we have to the peer. | ||
// TODO: Prefer some transports over others. Currently, we just select | ||
// the newest non-closed connection with the most streams. | ||
|
||
// TODO: Prefer some transports over others. | ||
// For now, prefers direct connections over Relayed connections. | ||
// For tie-breaking, select the newest non-closed connection with the most streams. | ||
vyzo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
s.conns.RLock() | ||
defer s.conns.RUnlock() | ||
|
||
var best *Conn | ||
bestLen := 0 | ||
for _, c := range s.conns.m[p] { | ||
for i := range s.conns.m[p] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so we don't use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that we are looping over a collection of reference types(pointers), it's easier to reason about the code if we use the reference semantic form of the range loop and avoid copies: https://www.ardanlabs.com/blog/2017/06/for-range-semantics.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is nonsense; we don't use the i and we iterate over connections. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
c := s.conns.m[p][i] | ||
if c.conn.IsClosed() { | ||
// We *will* garbage collect this soon anyways. | ||
continue | ||
|
@@ -395,15 +398,25 @@ func (s *Swarm) bestConnToPeer(p peer.ID) *Conn { | |
cLen := len(c.streams.m) | ||
c.streams.Unlock() | ||
|
||
if cLen >= bestLen { | ||
// We will never prefer a Relayed connection over a direct connection. | ||
if isDirectConn(best) && !isDirectConn(c) { | ||
continue | ||
} | ||
|
||
// 1. Always prefer a direct connection over a relayed connection. | ||
// 2. If both conns are direct or relayed, pick the one with as many or more streams. | ||
if (!isDirectConn(best) && isDirectConn(c)) || (cLen >= bestLen) { | ||
best = c | ||
bestLen = cLen | ||
} | ||
|
||
} | ||
return best | ||
} | ||
|
||
func isDirectConn(c *Conn) bool { | ||
return c != nil && !c.conn.Transport().Proxy() | ||
aarshkshah1992 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// Connectedness returns our "connectedness" state with the given peer. | ||
// | ||
// To check if we have an open connection, use `s.Connectedness(p) == | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -251,9 +251,14 @@ func (s *Swarm) dialPeer(ctx context.Context, p peer.ID) (*Conn, error) { | |
|
||
defer log.EventBegin(ctx, "swarmDialAttemptSync", p).Done() | ||
|
||
// check if we already have an open connection first | ||
conn := s.bestConnToPeer(p) | ||
if conn != nil { | ||
forceDirect, _ := network.GetForceDirectDial(ctx) | ||
if forceDirect { | ||
vyzo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if isDirectConn(conn) { | ||
return conn, nil | ||
} | ||
} else if conn != nil { | ||
// check if we already have an open connection first | ||
return conn, nil | ||
} | ||
|
||
|
@@ -287,8 +292,13 @@ func (s *Swarm) doDial(ctx context.Context, p peer.ID) (*Conn, error) { | |
// Short circuit. | ||
// By the time we take the dial lock, we may already *have* a connection | ||
// to the peer. | ||
forceDirect, _ := network.GetForceDirectDial(ctx) | ||
c := s.bestConnToPeer(p) | ||
if c != nil { | ||
if forceDirect { | ||
if isDirectConn(c) { | ||
return c, nil | ||
} | ||
} else if c != nil { | ||
return c, nil | ||
} | ||
|
||
|
@@ -301,15 +311,19 @@ func (s *Swarm) doDial(ctx context.Context, p peer.ID) (*Conn, error) { | |
conn, err := s.dial(ctx, p) | ||
if err != nil { | ||
conn = s.bestConnToPeer(p) | ||
if conn != nil { | ||
if forceDirect { | ||
if isDirectConn(conn) { | ||
log.Debugf("ignoring dial error because we have a connection: %s", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this log could be better, perhaps ".. because we already have a direct connection ..."? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
return conn, nil | ||
} | ||
} else if conn != nil { | ||
// Hm? What error? | ||
// Could have canceled the dial because we received a | ||
// connection or some other random reason. | ||
// Just ignore the error and return the connection. | ||
log.Debugf("ignoring dial error because we have a connection: %s", err) | ||
return conn, nil | ||
} | ||
|
||
vyzo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// ok, we failed. | ||
return nil, err | ||
} | ||
|
@@ -321,6 +335,11 @@ func (s *Swarm) canDial(addr ma.Multiaddr) bool { | |
return t != nil && t.CanDial(addr) | ||
} | ||
|
||
func (s *Swarm) nonProxyAddr(addr ma.Multiaddr) bool { | ||
t := s.TransportForDialing(addr) | ||
return !t.Proxy() | ||
Comment on lines
+340
to
+341
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could panic. I know we don't call it in a way that can panic, but it could still panic and someone else could call it. |
||
} | ||
|
||
// ranks addresses in descending order of preference for dialing | ||
// Private UDP > Public UDP > Private TCP > Public TCP > UDP Relay server > TCP Relay server | ||
func (s *Swarm) rankAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { | ||
|
@@ -362,6 +381,7 @@ func (s *Swarm) rankAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { | |
|
||
// dial is the actual swarm's dial logic, gated by Dial. | ||
func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) { | ||
forceDirect, _ := network.GetForceDirectDial(ctx) | ||
var logdial = lgbl.Dial("swarm", s.LocalPeer(), p, nil, nil) | ||
if p == s.local { | ||
log.Event(ctx, "swarmDialDoDialSelf", logdial) | ||
|
@@ -383,20 +403,26 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) { | |
return nil, &DialError{Peer: p, Cause: ErrNoAddresses} | ||
} | ||
goodAddrs := s.filterKnownUndialables(p, peerAddrs) | ||
if forceDirect { | ||
goodAddrs = addrutil.FilterAddrs(goodAddrs, s.nonProxyAddr) | ||
} | ||
if len(goodAddrs) == 0 { | ||
return nil, &DialError{Peer: p, Cause: ErrNoGoodAddresses} | ||
} | ||
|
||
/////// Check backoff andnRank addresses | ||
var nonBackoff bool | ||
for _, a := range goodAddrs { | ||
// skip addresses in back-off | ||
if !s.backf.Backoff(p, a) { | ||
nonBackoff = true | ||
// TODO How do we do backoff for forcedirect | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this needs to be addressed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly, I think we should just NOT do backoffs for forced direct conns at all. We only do this during hole punching anyways and a hole punching attempt can fail for a variety of reasons other than the address being bad. @Stebalien Any thoughts ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, fair enough, I am fine with it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed this TODO. |
||
if !forceDirect { | ||
/////// Check backoff andnRank addresses | ||
var nonBackoff bool | ||
for _, a := range goodAddrs { | ||
// skip addresses in back-off | ||
if !s.backf.Backoff(p, a) { | ||
nonBackoff = true | ||
} | ||
} | ||
if !nonBackoff { | ||
return nil, ErrDialBackoff | ||
} | ||
} | ||
if !nonBackoff { | ||
return nil, ErrDialBackoff | ||
} | ||
|
||
connC, dialErr := s.dialAddrs(ctx, p, s.rankAddrs(goodAddrs)) | ||
|
@@ -516,7 +542,6 @@ dialLoop: | |
remoteAddrChan = nil | ||
continue | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. restore this line pls. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. |
||
s.limitedDial(ctx, p, addr, respch) | ||
active++ | ||
case <-ctx.Done(): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to use the background context. Otherwise, if the first dialer cancels, we'll cancel the overall dial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also why the tests are failing.