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>
This commit is contained in:
@@ -1563,6 +1563,7 @@ func (c *Conn) maybeRebindOnError(err error) {
|
|||||||
|
|
||||||
if c.lastErrRebind.Load().Before(time.Now().Add(-5 * time.Second)) {
|
if c.lastErrRebind.Load().Before(time.Now().Add(-5 * time.Second)) {
|
||||||
c.logf("magicsock: performing rebind due to %q", reason)
|
c.logf("magicsock: performing rebind due to %q", reason)
|
||||||
|
c.lastErrRebind.Store(time.Now())
|
||||||
c.Rebind()
|
c.Rebind()
|
||||||
go c.ReSTUN(reason)
|
go c.ReSTUN(reason)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
"testing/synctest"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -3114,18 +3115,35 @@ func TestMaybeRebindOnError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("no-frequent-rebind", func(t *testing.T) {
|
t.Run("no-frequent-rebind", func(t *testing.T) {
|
||||||
if runtime.GOOS != "plan9" {
|
synctest.Test(t, func(t *testing.T) {
|
||||||
err := fmt.Errorf("outer err: %w", syscall.EPERM)
|
if runtime.GOOS != "plan9" {
|
||||||
conn := newTestConn(t)
|
err := fmt.Errorf("outer err: %w", syscall.EPERM)
|
||||||
defer conn.Close()
|
conn := newTestConn(t)
|
||||||
conn.lastErrRebind.Store(time.Now().Add(-1 * time.Second))
|
defer conn.Close()
|
||||||
before := metricRebindCalls.Value()
|
lastRebindTime := time.Now().Add(-1 * time.Second)
|
||||||
conn.maybeRebindOnError(err)
|
conn.lastErrRebind.Store(lastRebindTime)
|
||||||
after := metricRebindCalls.Value()
|
before := metricRebindCalls.Value()
|
||||||
if before != after {
|
conn.maybeRebindOnError(err)
|
||||||
t.Errorf("should not rebind within 5 seconds of last")
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user