Files
tsnet-proxy/vendor/tailscale.com/wgengine/connreject_pendopen.go
T
2026-10-22 12:27:19 +01:00

96 lines
3.0 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ts_omit_debug && !ts_omit_connreject
package wgengine
import (
"tailscale.com/net/connreject"
"tailscale.com/net/flowtrack"
"tailscale.com/net/packet"
)
// notifyConnRejectTSMPRecv emits an Outgoing-direction event derived
// from an inbound TSMP rejected header, if a callback is installed.
func (e *userspaceEngine) notifyConnRejectTSMPRecv(rh packet.TailscaleRejectedHeader) {
fn := e.connReject.cb.Load()
if fn == nil {
return
}
fn(connreject.Event{
Direction: connreject.Outgoing,
Proto: rh.Proto,
Src: rh.Src,
Dst: rh.Dst,
Reason: rejectReasonToReason(rh.Reason),
Source: connreject.SourceTSMPRecv,
MaybeBroken: rh.MaybeBroken,
})
}
// notifyConnRejectOpenTimeout classifies a pendopen-timeout diagnosis
// and emits an Outgoing-direction event, if a callback is installed and
// the classification is not deliberately suppressed (see
// [classifyOpenTimeout]).
func (e *userspaceEngine) notifyConnRejectOpenTimeout(flow flowtrack.Tuple, d openTimeoutDiag) {
fn := e.connReject.cb.Load()
if fn == nil {
return
}
reason, source := classifyOpenTimeout(d)
if reason == "" {
return
}
fn(connreject.Event{
Direction: connreject.Outgoing,
Proto: flow.Proto(),
Src: flow.Src(),
Dst: flow.Dst(),
Reason: reason,
Source: source,
})
}
// classifyOpenTimeout maps an [openTimeoutDiag] to a [connreject.Reason]
// and [connreject.Source] for emission. A returned reason of "" means
// the caller should not emit any event (used for the deliberately-silent
// onlyZeroRoute case).
//
// If d.problem is set, the peer's TSMP-reported reason supersedes our
// own diagnosis and the event is tagged SourceTSMPRecv (the timeout
// confirms the previously non-terminal reject was actually terminal).
func classifyOpenTimeout(d openTimeoutDiag) (connreject.Reason, connreject.Source) {
if d.onlyZeroRoute {
return "", connreject.SourceUnknown
}
if !d.problem.IsZero() {
return rejectReasonToReason(d.problem), connreject.SourceTSMPRecv
}
switch {
case d.noPeer:
return connreject.ReasonNoPeer, connreject.SourcePendOpenTimeout
case d.peerUnreachable:
return connreject.ReasonPeerUnreachable, connreject.SourcePendOpenTimeout
}
return connreject.ReasonTimeout, connreject.SourcePendOpenTimeout
}
// rejectReasonToReason maps a [packet.TailscaleRejectReason] to its
// corresponding [connreject.Reason] tag.
func rejectReasonToReason(r packet.TailscaleRejectReason) connreject.Reason {
switch r {
case packet.RejectedDueToACLs:
return connreject.ReasonACL
case packet.RejectedDueToShieldsUp:
return connreject.ReasonShields
case packet.RejectedDueToIPForwarding:
return connreject.ReasonHostIPForwarding
case packet.RejectedDueToHostFirewall:
return connreject.ReasonHostFirewall
case packet.RejectedDueToUnknownAppConnectorTransitIP:
return connreject.ReasonAppConnectorTransitIPUnknown
}
return connreject.ReasonUnknown
}