diff --git a/net/neterror/neterror.go b/net/neterror/neterror.go index e2387440d33d5..abdcf093fcf06 100644 --- a/net/neterror/neterror.go +++ b/net/neterror/neterror.go @@ -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 @@ -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 { diff --git a/net/neterror/neterror_windows.go b/net/neterror/neterror_windows.go index bf112f5ed7ab7..55eba260d24f9 100644 --- a/net/neterror/neterror_windows.go +++ b/net/neterror/neterror_windows.go @@ -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) + } } diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index b27736822af30..72c356a3c6d38 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -1236,6 +1236,12 @@ func (c *Conn) mkReceiveFunc(ruc *RebindingUDPConn, healthItem *health.ReceiveFu if neterror.PacketWasTruncated(err) { continue } + if neterror.SocketWasReset(err) { + c.logf("magicsock: receive: rebind required due to socket reset: %v", err) + c.Rebind() + c.ReSTUN("socket-reset") + } + return 0, err }