-
Notifications
You must be signed in to change notification settings - Fork 37
Support for Hole punching #233
Changes from 7 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,9 +379,10 @@ 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. | ||
// 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() | ||
|
||
|
@@ -395,15 +397,32 @@ func (s *Swarm) bestConnToPeer(p peer.ID) *Conn { | |
cLen := len(c.streams.m) | ||
c.streams.Unlock() | ||
|
||
if cLen >= bestLen { | ||
best = c | ||
bestLen = cLen | ||
if isDirectConn(best) { | ||
// Since the best connection we have till now is a direct connection, | ||
// we will ONLY replace it with a direct connection | ||
if !isDirectConn(c) { | ||
continue | ||
} | ||
if cLen >= bestLen { | ||
best = c | ||
bestLen = cLen | ||
} | ||
} else { | ||
// Since the best connection we have till now is a Relayed connection, | ||
// we will simply replace with a newer connection if it has atleast as many streams. | ||
if cLen >= bestLen { | ||
best = c | ||
bestLen = cLen | ||
} | ||
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. Probably easier: if isDirectConn(best) && !isDirectConn(c) {
continue
}
if cLen >= bestLen {
best = c
bestLen = cLen
} 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. Have implemented something similar now. 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. Actually this is not correct: we always want to replace a relayed connection with a direct connection, regardless of how many streams it has. 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. Ah, missed this. Have fixed it. |
||
} | ||
|
||
} | ||
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,28 @@ func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) { | |
return nil, &DialError{Peer: p, Cause: ErrNoAddresses} | ||
} | ||
goodAddrs := s.filterKnownUndialables(p, peerAddrs) | ||
// TODO Consider dialing private addresses as well | ||
// See https://github.com/libp2p/specs/pull/173/files#r288784559 | ||
if forceDirect { | ||
goodAddrs = addrutil.FilterAddrs(goodAddrs, manet.IsPublicAddr, s.nonProxyAddr) | ||
} | ||
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. hrm... yeah, this could be problematic here. If we are behind the same NAT we want the private dial. 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. |
||
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 +544,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.