wgengine/magicsock: fix check for EPERM on macOS

Like Linux, macOS will reply to sendto(2) with EPERM if the firewall is
currently blocking writes, though this behavior is like Linux
undocumented. This is often caused by a faulting network extension or
content filter from EDR software.

Updates #11710
Updates #12891
Updates #13511

Signed-off-by: James Tucker <james@tailscale.com>
raggi/eperm-health
James Tucker 2 months ago committed by James Tucker
parent 717d589149
commit 9eb59c72c1

@ -1149,8 +1149,8 @@ func (c *Conn) sendUDP(ipp netip.AddrPort, b []byte) (sent bool, err error) {
// maybeRebindOnError performs a rebind and restun if the error is defined and // maybeRebindOnError performs a rebind and restun if the error is defined and
// any conditionals are met. // any conditionals are met.
func (c *Conn) maybeRebindOnError(os string, err error) bool { func (c *Conn) maybeRebindOnError(os string, err error) bool {
switch err { switch {
case syscall.EPERM: case errors.Is(err, syscall.EPERM):
why := "operation-not-permitted-rebind" why := "operation-not-permitted-rebind"
switch os { switch os {
// We currently will only rebind and restun on a syscall.EPERM if it is experienced // We currently will only rebind and restun on a syscall.EPERM if it is experienced

@ -2965,26 +2965,31 @@ func TestMaybeRebindOnError(t *testing.T) {
tstest.PanicOnLog() tstest.PanicOnLog()
tstest.ResourceCheck(t) tstest.ResourceCheck(t)
conn := newTestConn(t) err := fmt.Errorf("outer err: %w", syscall.EPERM)
defer conn.Close()
t.Run("darwin-rebind", func(t *testing.T) { t.Run("darwin-rebind", func(t *testing.T) {
rebound := conn.maybeRebindOnError("darwin", syscall.EPERM) conn := newTestConn(t)
defer conn.Close()
rebound := conn.maybeRebindOnError("darwin", err)
if !rebound { if !rebound {
t.Errorf("darwin should rebind on syscall.EPERM") t.Errorf("darwin should rebind on syscall.EPERM")
} }
}) })
t.Run("linux-not-rebind", func(t *testing.T) { t.Run("linux-not-rebind", func(t *testing.T) {
rebound := conn.maybeRebindOnError("linux", syscall.EPERM) conn := newTestConn(t)
defer conn.Close()
rebound := conn.maybeRebindOnError("linux", err)
if rebound { if rebound {
t.Errorf("linux should not rebind on syscall.EPERM") t.Errorf("linux should not rebind on syscall.EPERM")
} }
}) })
t.Run("no-frequent-rebind", func(t *testing.T) { t.Run("no-frequent-rebind", func(t *testing.T) {
conn := newTestConn(t)
defer conn.Close()
conn.lastEPERMRebind.Store(time.Now().Add(-1 * time.Second)) conn.lastEPERMRebind.Store(time.Now().Add(-1 * time.Second))
rebound := conn.maybeRebindOnError("darwin", syscall.EPERM) rebound := conn.maybeRebindOnError("darwin", err)
if rebound { if rebound {
t.Errorf("darwin should not rebind on syscall.EPERM within 5 seconds of last") t.Errorf("darwin should not rebind on syscall.EPERM within 5 seconds of last")
} }

Loading…
Cancel
Save