wgengine/magicsock: rebind/restun if a syscall.EPERM error is returned (#11711)
We have seen in macOS client logs that the "operation not permitted", a syscall.EPERM error, is being returned when traffic is attempted to be sent. This may be caused by security software on the client. This change will perform a rebind and restun if we receive a syscall.EPERM error on clients running darwin. Rebinds will only be called if we haven't performed one specifically for an EPERM error in the past 5 seconds. Updates #11710 Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
This commit is contained in:

committed by
GitHub

parent
14c8b674ea
commit
449f46c207
@ -18,6 +18,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/tailscale/wireguard-go/conn"
|
||||
@ -305,6 +306,10 @@ type Conn struct {
|
||||
// getPeerByKey optionally specifies a function to look up a peer's
|
||||
// wireguard state by its public key. If nil, it's not used.
|
||||
getPeerByKey func(key.NodePublic) (_ wgint.Peer, ok bool)
|
||||
|
||||
// lastEPERMRebind tracks the last time a rebind was performed
|
||||
// after experiencing a syscall.EPERM.
|
||||
lastEPERMRebind syncs.AtomicValue[time.Time]
|
||||
}
|
||||
|
||||
// SetDebugLoggingEnabled controls whether spammy debug logging is enabled.
|
||||
@ -1047,6 +1052,8 @@ func (c *Conn) sendUDPBatch(addr netip.AddrPort, buffs [][]byte) (sent bool, err
|
||||
if errors.As(err, &errGSO) {
|
||||
c.logf("magicsock: %s", errGSO.Error())
|
||||
err = errGSO.RetryErr
|
||||
} else {
|
||||
_ = c.maybeRebindOnError(runtime.GOOS, err)
|
||||
}
|
||||
}
|
||||
return err == nil, err
|
||||
@ -1061,6 +1068,7 @@ func (c *Conn) sendUDP(ipp netip.AddrPort, b []byte) (sent bool, err error) {
|
||||
sent, err = c.sendUDPStd(ipp, b)
|
||||
if err != nil {
|
||||
metricSendUDPError.Add(1)
|
||||
_ = c.maybeRebindOnError(runtime.GOOS, err)
|
||||
} else {
|
||||
if sent {
|
||||
metricSendUDP.Add(1)
|
||||
@ -1069,6 +1077,34 @@ func (c *Conn) sendUDP(ipp netip.AddrPort, b []byte) (sent bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// maybeRebindOnError performs a rebind and restun if the error is defined and
|
||||
// any conditionals are met.
|
||||
func (c *Conn) maybeRebindOnError(os string, err error) bool {
|
||||
switch err {
|
||||
case syscall.EPERM:
|
||||
why := "operation-not-permitted-rebind"
|
||||
switch os {
|
||||
// We currently will only rebind and restun on a syscall.EPERM if it is experienced
|
||||
// on a client running darwin.
|
||||
// TODO(charlotte, raggi): expand os options if required.
|
||||
case "darwin":
|
||||
// TODO(charlotte): implement a backoff, so we don't end up in a rebind loop for persistent
|
||||
// EPERMs.
|
||||
if c.lastEPERMRebind.Load().Before(time.Now().Add(-5 * time.Second)) {
|
||||
c.logf("magicsock: performing %q", why)
|
||||
c.lastEPERMRebind.Store(time.Now())
|
||||
c.Rebind()
|
||||
go c.ReSTUN(why)
|
||||
return true
|
||||
}
|
||||
default:
|
||||
c.logf("magicsock: not performing %q", why)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sendUDP sends UDP packet b to addr.
|
||||
// See sendAddr's docs on the return value meanings.
|
||||
func (c *Conn) sendUDPStd(addr netip.AddrPort, b []byte) (sent bool, err error) {
|
||||
|
Reference in New Issue
Block a user