|
|
@ -1851,7 +1851,7 @@ func (c *Conn) handleDiscoMessage(msg []byte, src netaddr.IPPort) bool {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if de != nil {
|
|
|
|
if de != nil {
|
|
|
|
c.logf("magicsock: disco: %v<-%v (%v, %v) got call-me-maybe", c.discoShort, de.discoShort, de.publicKey.ShortString(), derpStr(src.String()))
|
|
|
|
c.logf("magicsock: disco: %v<-%v (%v, %v) got call-me-maybe", c.discoShort, de.discoShort, de.publicKey.ShortString(), derpStr(src.String()))
|
|
|
|
go de.handleCallMeMaybe()
|
|
|
|
go de.handleCallMeMaybe(dm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1882,6 +1882,29 @@ func (c *Conn) handlePingLocked(dm *disco.Ping, de *discoEndpoint, src netaddr.I
|
|
|
|
}, discoVerboseLog)
|
|
|
|
}, discoVerboseLog)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// enqueueCallMeMaybe schedules a send of disco.CallMeMaybe to de via derpAddr
|
|
|
|
|
|
|
|
// once we know that our STUN endpoint is fresh.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// derpAddr is de.derpAddr at the time of send. It's assumed the peer won't be
|
|
|
|
|
|
|
|
// flipping primary DERPs in the 0-30ms it takes to confirm our STUN endpoint.
|
|
|
|
|
|
|
|
// If they do, traffic will just go over DERP for a bit longer until the next
|
|
|
|
|
|
|
|
// discovery round.
|
|
|
|
|
|
|
|
func (c *Conn) enqueueCallMeMaybe(derpAddr netaddr.IPPort, de *discoEndpoint) {
|
|
|
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO(bradfitz): do a fast/lite re-STUN if our STUN info is too old
|
|
|
|
|
|
|
|
// Currently there's no "queue" despite the method name. There will be.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eps := make([]netaddr.IPPort, 0, len(c.lastEndpoints))
|
|
|
|
|
|
|
|
for _, ep := range c.lastEndpoints {
|
|
|
|
|
|
|
|
if ipp, err := netaddr.ParseIPPort(ep); err == nil {
|
|
|
|
|
|
|
|
eps = append(eps, ipp)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
go de.sendDiscoMessage(derpAddr, &disco.CallMeMaybe{MyNumber: eps}, discoLog)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// setAddrToDiscoLocked records that newk is at src.
|
|
|
|
// setAddrToDiscoLocked records that newk is at src.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// c.mu must be held.
|
|
|
|
// c.mu must be held.
|
|
|
@ -2841,6 +2864,7 @@ type discoEndpoint struct {
|
|
|
|
trustBestAddrUntil time.Time // time when bestAddr expires
|
|
|
|
trustBestAddrUntil time.Time // time when bestAddr expires
|
|
|
|
sentPing map[stun.TxID]sentPing
|
|
|
|
sentPing map[stun.TxID]sentPing
|
|
|
|
endpointState map[netaddr.IPPort]*endpointState
|
|
|
|
endpointState map[netaddr.IPPort]*endpointState
|
|
|
|
|
|
|
|
isCallMeMaybeEP map[netaddr.IPPort]bool
|
|
|
|
|
|
|
|
|
|
|
|
pendingCLIPings []pendingCLIPing // any outstanding "tailscale ping" commands running
|
|
|
|
pendingCLIPings []pendingCLIPing // any outstanding "tailscale ping" commands running
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -2905,6 +2929,10 @@ type endpointState struct {
|
|
|
|
// updated and use it to discard old candidates.
|
|
|
|
// updated and use it to discard old candidates.
|
|
|
|
lastGotPing time.Time
|
|
|
|
lastGotPing time.Time
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// callMeMaybeTime, if non-zero, is the time this endpoint
|
|
|
|
|
|
|
|
// was advertised last via a call-me-maybe disco message.
|
|
|
|
|
|
|
|
callMeMaybeTime time.Time
|
|
|
|
|
|
|
|
|
|
|
|
recentPongs []pongReply // ring buffer up to pongHistoryCount entries
|
|
|
|
recentPongs []pongReply // ring buffer up to pongHistoryCount entries
|
|
|
|
recentPong uint16 // index into recentPongs of most recent; older before, wrapped
|
|
|
|
recentPong uint16 // index into recentPongs of most recent; older before, wrapped
|
|
|
|
|
|
|
|
|
|
|
@ -2918,11 +2946,13 @@ const indexSentinelDeleted = -1
|
|
|
|
// shouldDeleteLocked reports whether we should delete this endpoint.
|
|
|
|
// shouldDeleteLocked reports whether we should delete this endpoint.
|
|
|
|
func (st *endpointState) shouldDeleteLocked() bool {
|
|
|
|
func (st *endpointState) shouldDeleteLocked() bool {
|
|
|
|
switch {
|
|
|
|
switch {
|
|
|
|
|
|
|
|
case !st.callMeMaybeTime.IsZero():
|
|
|
|
|
|
|
|
return false
|
|
|
|
case st.lastGotPing.IsZero():
|
|
|
|
case st.lastGotPing.IsZero():
|
|
|
|
// This was an endpoint from the network map. Is it still in the network map?
|
|
|
|
// This was an endpoint from the network map. Is it still in the network map?
|
|
|
|
return st.index == indexSentinelDeleted
|
|
|
|
return st.index == indexSentinelDeleted
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
// Thiw was an endpoint discovered at runtime.
|
|
|
|
// This was an endpoint discovered at runtime.
|
|
|
|
return time.Since(st.lastGotPing) > sessionActiveTimeout
|
|
|
|
return time.Since(st.lastGotPing) > sessionActiveTimeout
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -3236,13 +3266,12 @@ func (de *discoEndpoint) sendPingsLocked(now time.Time, sendCallMeMaybe bool) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
derpAddr := de.derpAddr
|
|
|
|
derpAddr := de.derpAddr
|
|
|
|
if sentAny && sendCallMeMaybe && !derpAddr.IsZero() {
|
|
|
|
if sentAny && sendCallMeMaybe && !derpAddr.IsZero() {
|
|
|
|
// In just a bit of a time (for goroutines above to schedule and run),
|
|
|
|
// Have our magicsock.Conn figure out its STUN endpoint (if
|
|
|
|
// send a message to peer via DERP informing them that we've sent
|
|
|
|
// it doesn't know already) and then send a CallMeMaybe
|
|
|
|
// so our firewall ports are probably open and now would be a good time
|
|
|
|
// message to our peer via DERP informing them that we've
|
|
|
|
// for them to connect.
|
|
|
|
// sent so our firewall ports are probably open and now
|
|
|
|
time.AfterFunc(5*time.Millisecond, func() {
|
|
|
|
// would be a good time for them to connect.
|
|
|
|
de.sendDiscoMessage(derpAddr, &disco.CallMeMaybe{}, discoLog)
|
|
|
|
go de.c.enqueueCallMeMaybe(derpAddr, de)
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -3424,10 +3453,34 @@ func (st *endpointState) addPongReplyLocked(r pongReply) {
|
|
|
|
// DERP. The contract for use of this message is that the peer has
|
|
|
|
// DERP. The contract for use of this message is that the peer has
|
|
|
|
// already sent to us via UDP, so their stateful firewall should be
|
|
|
|
// already sent to us via UDP, so their stateful firewall should be
|
|
|
|
// open. Now we can Ping back and make it through.
|
|
|
|
// open. Now we can Ping back and make it through.
|
|
|
|
func (de *discoEndpoint) handleCallMeMaybe() {
|
|
|
|
func (de *discoEndpoint) handleCallMeMaybe(m *disco.CallMeMaybe) {
|
|
|
|
de.mu.Lock()
|
|
|
|
de.mu.Lock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
defer de.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
for ep := range de.isCallMeMaybeEP {
|
|
|
|
|
|
|
|
de.isCallMeMaybeEP[ep] = false // mark for deletion
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if de.isCallMeMaybeEP == nil {
|
|
|
|
|
|
|
|
de.isCallMeMaybeEP = map[netaddr.IPPort]bool{}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, ep := range m.MyNumber {
|
|
|
|
|
|
|
|
de.isCallMeMaybeEP[ep] = true
|
|
|
|
|
|
|
|
if es, ok := de.endpointState[ep]; ok {
|
|
|
|
|
|
|
|
es.callMeMaybeTime = now
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
de.endpointState[ep] = &endpointState{callMeMaybeTime: now}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete any prior CalllMeMaybe endpoints that weren't included
|
|
|
|
|
|
|
|
// in this message.
|
|
|
|
|
|
|
|
for ep, want := range de.isCallMeMaybeEP {
|
|
|
|
|
|
|
|
if !want {
|
|
|
|
|
|
|
|
delete(de.isCallMeMaybeEP, ep)
|
|
|
|
|
|
|
|
de.deleteEndpointLocked(ep)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Zero out all the lastPing times to force sendPingsLocked to send new ones,
|
|
|
|
// Zero out all the lastPing times to force sendPingsLocked to send new ones,
|
|
|
|
// even if it's been less than 5 seconds ago.
|
|
|
|
// even if it's been less than 5 seconds ago.
|
|
|
|
for _, st := range de.endpointState {
|
|
|
|
for _, st := range de.endpointState {
|
|
|
|