Skip to content

Commit

Permalink
fix: immediately rebind after WSAECONNRESET
Browse files Browse the repository at this point in the history
  • Loading branch information
deansheather committed Sep 16, 2024
1 parent aa558fb commit 9859a42
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 1 deletion.
16 changes: 15 additions & 1 deletion net/neterror/neterror.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ func TreatAsLostUDP(err error) bool {
return false
}

var packetWasTruncated func(error) bool // non-nil on Windows at least
var (
packetWasTruncated func(error) bool // non-nil on Windows at least
socketWasReset func(error) bool // non-nil on Windows at least
)

// PacketWasTruncated reports whether err indicates truncation but the RecvFrom
// that generated err was otherwise successful. On Windows, Go's UDP RecvFrom
Expand All @@ -59,6 +62,17 @@ func PacketWasTruncated(err error) bool {
return packetWasTruncated(err)
}

// SocketWasReset reports whether err is an error from a TCP or UDP send/recv
// operation that should be treated as a connection reset. On Windows,
// WSARecvFrom can return WSAECONNRESET when the remote side sends an ICMP error
// message.
func SocketWasReset(err error) bool {
if socketWasReset == nil {
return false
}
return socketWasReset(err)
}

var shouldDisableUDPGSO func(error) bool // non-nil on Linux

func ShouldDisableUDPGSO(err error) bool {
Expand Down
4 changes: 4 additions & 0 deletions net/neterror/neterror_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ func init() {
packetWasTruncated = func(err error) bool {
return errors.Is(err, windows.WSAEMSGSIZE)
}

socketWasReset = func(err error) bool {
return errors.Is(err, windows.WSAECONNRESET)
}
}
24 changes: 24 additions & 0 deletions wgengine/magicsock/magicsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -1213,17 +1213,32 @@ func (c *Conn) receiveIPv6() conn.ReceiveFunc {
return c.mkReceiveFunc(&c.pconn6, &health.ReceiveIPv6, metricRecvDataIPv6)
}

type permanentNetError struct {
error
}

var _ net.Error = permanentNetError{}

func (permanentNetError) Timeout() bool { return false }
func (permanentNetError) Temporary() bool { return false }

// mkReceiveFunc creates a ReceiveFunc reading from ruc.
// The provided healthItem and metric are updated if non-nil.
func (c *Conn) mkReceiveFunc(ruc *RebindingUDPConn, healthItem *health.ReceiveFuncStats, metric *clientmetric.Metric) conn.ReceiveFunc {
// epCache caches an IPPort->endpoint for hot flows.
var epCache ippEndpointCache
var connErr error

return func(buffs [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
if healthItem != nil {
healthItem.Enter()
defer healthItem.Exit()
}
if connErr != nil {
// Just in case we get another call, we don't want to call ReadBatch
// again.
return 0, connErr
}
if ruc == nil {
panic("nil RebindingUDPConn")
}
Expand All @@ -1236,6 +1251,15 @@ func (c *Conn) mkReceiveFunc(ruc *RebindingUDPConn, healthItem *health.ReceiveFu
if neterror.PacketWasTruncated(err) {
continue
}
if neterror.SocketWasReset(err) {
// Wrap the error in a permanentNetError so that Wireguard
// doesn't keep trying to read packets from us.
connErr = permanentNetError{error: err}
c.logf("magicsock: receive: rebind required due to socket reset: %v", err)
c.Rebind()
return 0, connErr
}

return 0, err
}

Expand Down

0 comments on commit 9859a42

Please sign in to comment.