wgengine/magicsock: fix rebind debouncing (#17282)

On platforms that are causing EPIPE at a high frequency this is
resulting in non-working connections, for example when Apple decides to
forcefully close UDP sockets due to an unsoliced packet rejection in the
firewall.

Too frequent rebinds cause a failure to solicit the endpoints triggering
the rebinds, that would normally happen via CallMeMaybe.

Updates #14551
Updates tailscale/corp#25648

Signed-off-by: James Tucker <james@tailscale.com>
pull/17293/head
James Tucker 3 months ago committed by GitHub
parent 41a2aaf1da
commit 8b3e88cd09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1563,6 +1563,7 @@ func (c *Conn) maybeRebindOnError(err error) {
if c.lastErrRebind.Load().Before(time.Now().Add(-5 * time.Second)) {
c.logf("magicsock: performing rebind due to %q", reason)
c.lastErrRebind.Store(time.Now())
c.Rebind()
go c.ReSTUN(reason)
} else {

@ -27,6 +27,7 @@ import (
"sync/atomic"
"syscall"
"testing"
"testing/synctest"
"time"
"unsafe"
@ -3114,18 +3115,35 @@ func TestMaybeRebindOnError(t *testing.T) {
}
t.Run("no-frequent-rebind", func(t *testing.T) {
if runtime.GOOS != "plan9" {
err := fmt.Errorf("outer err: %w", syscall.EPERM)
conn := newTestConn(t)
defer conn.Close()
conn.lastErrRebind.Store(time.Now().Add(-1 * time.Second))
before := metricRebindCalls.Value()
conn.maybeRebindOnError(err)
after := metricRebindCalls.Value()
if before != after {
t.Errorf("should not rebind within 5 seconds of last")
synctest.Test(t, func(t *testing.T) {
if runtime.GOOS != "plan9" {
err := fmt.Errorf("outer err: %w", syscall.EPERM)
conn := newTestConn(t)
defer conn.Close()
lastRebindTime := time.Now().Add(-1 * time.Second)
conn.lastErrRebind.Store(lastRebindTime)
before := metricRebindCalls.Value()
conn.maybeRebindOnError(err)
after := metricRebindCalls.Value()
if before != after {
t.Errorf("should not rebind within 5 seconds of last")
}
// ensure that rebinds are performed and store an updated last
// rebind time.
time.Sleep(6 * time.Second)
conn.maybeRebindOnError(err)
newTime := conn.lastErrRebind.Load()
if newTime == lastRebindTime {
t.Errorf("expected a rebind to occur")
}
if newTime.Sub(lastRebindTime) < 5*time.Second {
t.Errorf("expected at least 5 seconds between %s and %s", lastRebindTime, newTime)
}
}
}
})
})
}

Loading…
Cancel
Save