/*
 * SPDX-License-Identifier: AGPL-3.0-only
 * Copyright (c) 2022-2026, daeuniverse Organization <dae@v2raya.org>
 */

package control

import (
	"context"
	stderrors "errors"
	"fmt"
	"net"
	"net/netip"
	"strings"
	"time"

	"github.com/daeuniverse/dae/common/consts"
	"github.com/daeuniverse/dae/common/errors"
	ob "github.com/daeuniverse/dae/component/outbound"
	"github.com/daeuniverse/dae/component/outbound/dialer"
	"github.com/daeuniverse/dae/component/sniffing"
	"github.com/daeuniverse/outbound/pool"
	dnsmessage "github.com/miekg/dns"
	"github.com/sirupsen/logrus"
	"golang.org/x/sys/unix"
)

func shouldTryRawUDPFallback(err error, from, realTo netip.AddrPort) bool {
	if err == nil {
		return false
	}
	fromAddr := from.Addr()
	toAddr := realTo.Addr()
	if (fromAddr.Is4() || fromAddr.Is4In6()) != (toAddr.Is4() || toAddr.Is4In6()) {
		return false
	}
	// Keep fallback scope narrow: only DNS responses (source port 53).
	if from.Port() != 53 {
		return false
	}
	if stderrors.Is(err, unix.EADDRINUSE) || stderrors.Is(err, unix.EADDRNOTAVAIL) {
		return true
	}
	// Some net stack paths wrap errno and lose Is(err, errno) matching.
	// Match common bind/send failures conservatively.
	errStr := strings.ToLower(err.Error())
	return strings.Contains(errStr, "address already in use") ||
		strings.Contains(errStr, "cannot assign requested address")
}

func tryRawUDPFallback(log *logrus.Logger, data []byte, from, realTo netip.AddrPort, debugEnabled, errorEnabled bool, reason string, err error) bool {
	if !shouldTryRawUDPFallback(err, from, realTo) {
		return false
	}
	var fallbackErr error
	if from.Addr().Is4() || from.Addr().Is4In6() {
		fallbackErr = sendUDPv4RawInDaeNetns(data, from, realTo)
	} else {
		fallbackErr = sendUDPv6RawInDaeNetns(data, from, realTo)
	}
	if fallbackErr == nil {
		if debugEnabled {
			log.WithFields(logrus.Fields{
				"from":   from.String(),
				"to":     realTo.String(),
				"reason": reason,
			}).Debug("sendPkt: used raw UDP fallback")
		}
		return true
	}
	if errorEnabled {
		log.WithFields(logrus.Fields{
			"from":     from.String(),
			"to":       realTo.String(),
			"reason":   reason,
			"trigger":  err.Error(),
			"fallback": fallbackErr.Error(),
		}).Error("sendPkt: raw UDP fallback failed")
	}
	return false
}

var (
	// DefaultNatTimeout is the default NAT timeout for UDP connections.
	// Reduced from 3 minutes to 30 seconds for faster resource cleanup.
	// Most DNS queries complete within seconds, and long-lived connections
	// (like QUIC) can use longer timeouts via QuicNatTimeout.
	DefaultNatTimeout = 30 * time.Second
	// QuicNatTimeout is 2 minutes for QUIC long-lived connections.
	QuicNatTimeout = 2 * time.Minute
)

const (
	DnsNatTimeout  = 17 * time.Second // RFC 5452
	AnyfromTimeout = 5 * time.Second  // Do not cache too long.
	MaxRetry       = 2

	connectionErrorLogInterval = 5 * time.Second
)

// ResetUdpLogLimiters clears all rate limiters for UDP logging.
// Called on reload to allow fresh logging after configuration changes.
func ResetUdpLogLimiters() {
}

func udpEndpointNetworkType(ue *UdpEndpoint) dialer.NetworkType {
	if ue != nil && ue.endpointNetworkType.L4Proto != "" {
		networkType := ue.endpointNetworkType
		if networkType.IpVersion == "" && ue.lAddr.IsValid() {
			networkType.IpVersion = consts.IpVersionFromAddr(ue.lAddr.Addr())
		}
		if networkType.L4Proto == consts.L4ProtoStr_UDP {
			networkType.IsDns = false
			if networkType.UdpHealthDomain == dialer.UdpHealthDomainUnset {
				networkType.UdpHealthDomain = dialer.UdpHealthDomainData
			}
		}
		return networkType
	}
	return dialer.NetworkType{
		L4Proto:         consts.L4ProtoStr_UDP,
		IpVersion:       consts.IpVersionFromAddr(ue.lAddr.Addr()),
		IsDns:           false,
		UdpHealthDomain: dialer.UdpHealthDomainData,
	}
}

func shouldRejectNewUdpDialSelection(res *proxyDialResult) bool {
	if res == nil || res.Dialer == nil {
		return false
	}
	networkType := res.SelectionNetworkTypeObj
	if res.AdmissionNetworkTypeObj != nil {
		networkType = res.AdmissionNetworkTypeObj
	}
	if networkType == nil || networkType.L4Proto != consts.L4ProtoStr_UDP {
		return false
	}
	if res.Outbound != nil && res.Outbound.GetSelectionPolicy() == consts.DialerSelectionPolicy_Fixed {
		return false
	}
	return !res.Dialer.MustGetAlive(networkType)
}

func (c *ControlPlane) checkUdpEndpointHealth(ue *UdpEndpoint, ueKey UdpEndpointKey, isFastPath bool) bool {
	if ue == nil || ue.Dialer == nil {
		return false
	}
	if ue.Outbound != nil && ue.Outbound.GetSelectionPolicy() == consts.DialerSelectionPolicy_Fixed {
		return true
	}
	if ue.hasSent.Load() || ue.hasReply.Load() {
		// Once an endpoint has forwarded real traffic, treat it as a live session.
		// Control-plane health should gate only new dial selection; existing UDP
		// sessions must be retired by actual data-plane failures or timeout, not
		// by probe jitter.
		return true
	}
	networkType := udpEndpointNetworkType(ue)

	// Only endpoints that have never forwarded a packet are safe to cull based
	// on health probes alone.
	if !ue.Dialer.MustGetAlive(&networkType) {
		if c.log.IsLevelEnabled(logrus.DebugLevel) {
			path := "UDP"
			if isFastPath {
				path = "fast-path UDP"
			}
			c.log.WithFields(logrus.Fields{
				"dialer": ue.Dialer.Property().Name,
				"alive":  ue.Dialer.MustGetAlive(&networkType),
			}).Debugf("Re-selecting outbound for existing %s endpoint due to dialer health.", path)
		}
		_ = DefaultUdpEndpointPool.Remove(ueKey, ue)
		return false
	}
	return true
}

func (c *ControlPlane) allowConnectionErrorLog(now time.Time) bool {
	nowNano := now.UnixNano()
	for {
		last := c.lastConnectionErrorLogTime.Load()
		if nowNano-last < int64(connectionErrorLogInterval) {
			return false
		}
		if c.lastConnectionErrorLogTime.CompareAndSwap(last, nowNano) {
			return true
		}
	}
}

type DialOption struct {
	Target        string
	Dialer        *dialer.Dialer
	Outbound      *ob.DialerGroup
	Network       string
	NetworkType   *dialer.NetworkType
	SniffedDomain string
	Excluded      *dialer.Dialer
	// NowNano is an optional pre-calculated timestamp to avoid calling time.Now()
	// in the hot path. If 0, time.Now() will be used.
	NowNano int64
}

func ChooseNatTimeout(data []byte, sniffDns bool) (dmsg *dnsmessage.Msg, timeout time.Duration) {
	if sniffDns {
		var dnsmsg dnsmessage.Msg
		if err := dnsmsg.Unpack(data); err == nil && !dnsmsg.Response && dnsmsg.Rcode == dnsmessage.RcodeSuccess {
			// log.Printf("DEBUG: lookup %v", dnsmsg.Question[0].Name)
			return &dnsmsg, DnsNatTimeout
		}
	}
	return nil, DefaultNatTimeout
}

func normalizeSendPktAddrFamily(from, realTo netip.AddrPort) (bindAddr, writeAddr netip.AddrPort) {
	bindAddr = from
	writeAddr = realTo

	// Pre-compute address types once for performance (avoid repeated method calls).
	// This ensures idempotency - multiple calls with same inputs produce same output.
	fromAddr := from.Addr()
	toAddr := realTo.Addr()

	fromIs4 := fromAddr.Is4()
	fromIs4In6 := fromAddr.Is4In6()
	toIs4 := toAddr.Is4()
	toIs4In6 := toAddr.Is4In6()
	fromIs6 := fromAddr.Is6() && !fromIs4In6
	toIs6 := toAddr.Is6() && !toIs4In6

	// Optimization 1: When both addresses are IPv4-compatible (pure or mapped),
	// unmap to pure IPv4 for better performance. AF_INET sockets are faster
	// than AF_INET6 and have lower overhead.
	if (fromIs4 || fromIs4In6) && (toIs4 || toIs4In6) {
		if fromIs4In6 {
			bindAddr = netip.AddrPortFrom(fromAddr.Unmap(), from.Port())
		}
		if toIs4In6 {
			writeAddr = netip.AddrPortFrom(toAddr.Unmap(), realTo.Port())
		}
		// Both addresses are now pure IPv4 - no further conversion needed.
		return bindAddr, writeAddr
	}

	// Case 2: IPv4 bind with IPv6 target → IPv6 wildcard.
	// Using IPv6 wildcard [::] instead of IPv4-mapped ensures:
	// - AnyfromPool creates a proper dual-stack socket
	// - Multiple IPv4 servers can share the same pool entry
	// - Better compatibility with multi-server scenarios
	if (fromIs4 || fromIs4In6) && toIs6 {
		bindAddr = netip.AddrPortFrom(netip.IPv6Unspecified(), from.Port())
		return bindAddr, writeAddr
	}

	// Case 3: IPv6 bind with IPv4 target → IPv4-mapped writeAddr.
	// This allows the AF_INET6 socket to send to IPv4 destinations.
	if fromIs6 && (toIs4 || toIs4In6) {
		writeAddr = netip.AddrPortFrom(netip.AddrFrom16(toAddr.As16()), realTo.Port())
		return bindAddr, writeAddr
	}

	// Fast path: same-family or already optimal.
	// No conversion needed - return as-is.
	return bindAddr, writeAddr
}

type udpEndpointReplySender func(log *logrus.Logger, data []byte, from netip.AddrPort, realTo netip.AddrPort, slot udpEndpointResponseConnSlot) error

type anyfromPtrSlot struct {
	ptr **Anyfrom
}

func (s anyfromPtrSlot) Load() *Anyfrom {
	if s.ptr == nil {
		return nil
	}
	return *s.ptr
}

func (s anyfromPtrSlot) Swap(next *Anyfrom) {
	if s.ptr == nil {
		return
	}
	swapPinnedAnyfrom(s.ptr, next)
}

func swapPinnedAnyfrom(slot **Anyfrom, next *Anyfrom) {
	if slot == nil {
		return
	}
	current := *slot
	if current == next {
		return
	}
	if next != nil {
		next.Pin()
	}
	*slot = next
	if current != nil {
		current.Unpin()
	}
}

func sendPktWithResponseConnSlot(log *logrus.Logger, data []byte, from netip.AddrPort, realTo netip.AddrPort, slot udpEndpointResponseConnSlot, cache udpEndpointResponseConnCache) (err error) {
	// Proxy chain support: Use original 'from' address as bindAddr to ensure
	// each server response gets its own UDP socket. This prevents response mixing
	// when multiple IPv6 servers would otherwise share [::]:port (wildcard binding).
	//
	// Cross-family handling ensures socket type matches write address family:
	// - IPv6->IPv4: Convert writeAddr to IPv4-mapped IPv6 for dual-stack socket
	// - IPv4->IPv6: Convert bindAddr to IPv4-mapped IPv6 to create IPv6 socket
	bindAddr, writeAddr := normalizeSendPktAddrFamily(from, realTo)
	traceEnabled := log != nil && log.IsLevelEnabled(logrus.TraceLevel)
	debugEnabled := log != nil && log.IsLevelEnabled(logrus.DebugLevel)
	errorEnabled := log != nil && log.IsLevelEnabled(logrus.ErrorLevel)

	if traceEnabled {
		log.WithFields(logrus.Fields{
			"from":       from.String(),
			"to":         realTo.String(),
			"bind_addr":  bindAddr.String(),
			"write_addr": writeAddr.String(),
			"data_size":  len(data),
		}).Trace("sendPkt: preparing to send UDP packet")
	}

	// Try cached socket first (for Symmetric NAT sessions)
	if slot != nil {
		if cached := slot.Load(); cached != nil {
			if _, err = cached.WriteToUDPAddrPort(data, writeAddr); err == nil {
				if traceEnabled {
					log.WithFields(logrus.Fields{
						"to":         realTo.String(),
						"write_addr": writeAddr.String(),
						"cached":     true,
					}).Trace("sendPkt: sent via cached socket")
				}
				return nil
			}
			// Cached socket is stale or broken; clear the cache slot immediately
			// so the next call doesn't waste time retrying a dead socket.
			slot.Swap(nil)
			if debugEnabled {
				log.WithFields(logrus.Fields{
					"error": err.Error(),
				}).Debug("sendPkt: cached socket failed, getting new socket from pool")
			}
		}
	}

	if cache != nil {
		if cached := cache.CachedResponseConn(bindAddr); cached != nil {
			if _, err = cached.WriteToUDPAddrPort(data, writeAddr); err == nil {
				if traceEnabled {
					log.WithFields(logrus.Fields{
						"to":         realTo.String(),
						"write_addr": writeAddr.String(),
						"cached":     true,
						"cache_kind": "bind_addr",
					}).Trace("sendPkt: sent via bind-address cached socket")
				}
				return nil
			}
			cache.ClearCachedResponseConn(bindAddr, cached)
			if debugEnabled {
				log.WithFields(logrus.Fields{
					"bind_addr": bindAddr.String(),
					"error":     err.Error(),
				}).Debug("sendPkt: bind-address cached socket failed, getting new socket from pool")
			}
		}
	}

	uConn, isNew, err := DefaultAnyfromPool.GetOrCreate(bindAddr, AnyfromTimeout)
	if err != nil {
		if tryRawUDPFallback(log, data, from, realTo, debugEnabled, errorEnabled, "get-or-create", err) {
			return nil
		}
		if stderrors.Is(err, ErrAnyfromBindFailed) {
			// Return the error instead of silently dropping. The caller decides
			// whether the packet loss is acceptable (reply path) or must be retried.
			return err
		}
		if errorEnabled {
			log.WithFields(logrus.Fields{
				"bind_addr": bindAddr.String(),
				"error":     err.Error(),
			}).Error("sendPkt: failed to get or create socket from pool")
		}
		return err
	}
	if traceEnabled {
		log.WithFields(logrus.Fields{
			"bind_addr":  bindAddr.String(),
			"new_socket": isNew,
		}).Trace("sendPkt: got socket from pool")
	}

	_, err = uConn.WriteToUDPAddrPort(data, writeAddr)
	if err != nil {
		if tryRawUDPFallback(log, data, from, realTo, debugEnabled, errorEnabled, "write-to-udp", err) {
			return nil
		}
		if errorEnabled {
			log.WithFields(logrus.Fields{
				"write_addr": writeAddr.String(),
				"data_size":  len(data),
				"error":      err.Error(),
			}).Error("sendPkt: WriteToUDPAddrPort failed")
		}
		return err
	}

	if traceEnabled {
		log.WithFields(logrus.Fields{
			"to":         realTo.String(),
			"write_addr": writeAddr.String(),
			"data_size":  len(data),
		}).Trace("sendPkt: successfully sent packet")
	}

	// Update caller's cached socket so future calls skip the pool lookup
	if slot != nil && err == nil {
		slot.Swap(uConn)
	}
	if cache != nil && err == nil {
		cache.StoreCachedResponseConn(bindAddr, uConn)
	}
	return err
}

func sendPktWithCacheProvider(log *logrus.Logger, data []byte, from netip.AddrPort, realTo netip.AddrPort, afp **Anyfrom, cache udpEndpointResponseConnCache) (err error) {
	var slot udpEndpointResponseConnSlot
	if afp != nil {
		slot = anyfromPtrSlot{ptr: afp}
	}
	return sendPktWithResponseConnSlot(log, data, from, realTo, slot, cache)
}

// sendPkt sends a UDP packet to the destination.
// Parameters:
//   - log: logger to use
//   - data: packet data to send
//   - from: source address of the packet (for logging/metadata only)
//   - realTo: destination address where the packet should be sent
//   - afp: optional cached Anyfrom socket for Symmetric NAT sessions
func sendPkt(log *logrus.Logger, data []byte, from netip.AddrPort, realTo netip.AddrPort, afp **Anyfrom) (err error) {
	return sendPktWithCacheProvider(log, data, from, realTo, afp, nil)
}

func forwardUdpEndpointReplyToClient(log *logrus.Logger, ue *UdpEndpoint, data []byte, from netip.AddrPort, clientAddr netip.AddrPort, send udpEndpointReplySender, recordDownload func(int64)) error {
	recordDownload = normalizeTrafficRecord(recordDownload)
	var cacheSlot udpEndpointResponseConnSlot
	var cacheProvider udpEndpointResponseConnCache
	if ue != nil {
		cacheSlot = ue.responseConnSlot()
		cacheProvider = ue
	}
	// Local reply reinjection failures do not mean the upstream proxy session is
	// broken. Keeping the endpoint alive avoids recreating a fresh UDP session
	// for every subsequent client packet after a transient local send failure.
	if send == nil {
		if err := sendPktWithResponseConnSlot(log, data, from, clientAddr, cacheSlot, cacheProvider); err != nil {
			if log != nil && log.IsLevelEnabled(logrus.DebugLevel) {
				log.WithFields(logrus.Fields{
					"from":      from.String(),
					"to":        clientAddr.String(),
					"data_size": len(data),
					"error":     err.Error(),
				}).Debug("forwardUdpEndpointReplyToClient: reply to client failed (packet dropped)")
			}
			return nil
		}
		recordDownload(int64(len(data)))
		return nil
	}
	if err := send(log, data, from, clientAddr, cacheSlot); err != nil {
		if log != nil && log.IsLevelEnabled(logrus.DebugLevel) {
			log.WithFields(logrus.Fields{
				"from":      from.String(),
				"to":        clientAddr.String(),
				"data_size": len(data),
				"error":     err.Error(),
			}).Debug("forwardUdpEndpointReplyToClient: reply to client failed (packet dropped)")
		}
		return nil
	}
	recordDownload(int64(len(data)))
	return nil
}

func (c *ControlPlane) handlePkt(lConn *net.UDPConn, data []byte, src, realDst netip.AddrPort, routingResult *bpfRoutingResult, flowDecision UdpFlowDecision, skipSniffing bool) (err error) {
	var realSrc netip.AddrPort
	var domain string
	var ueKey UdpEndpointKey
	var excludedDialer *dialer.Dialer
	now := time.Now()
	nowNano := now.UnixNano()
	realSrc = src
	routeScope := udpEndpointRouteScope{}
	forceSymmetricKey := false
	if c.udpRouteScopeSensitive {
		routeScope = newUdpEndpointRouteScope(routingResult)
		forceSymmetricKey = udpRouteScopeNeedsDestinationAffinity(routingResult)
	}

	// DNS Fast Path: Skip UdpEndpoint lookup for DNS traffic (port 53).
	// DNS is a stateless protocol and doesn't need the connection tracking
	// features that UdpEndpoint provides (designed for QUIC and other long-lived UDP).
	// This optimization eliminates a sync.Map.Load() operation for every DNS query.
	if realDst.Port() == 53 {
		// Potential DNS query - verify with DNS message parsing
		dnsMessage, _ := ChooseNatTimeout(data, true)
		if dnsMessage != nil {
			// Confirmed DNS request - take fast path
			if routingResult.Mark == 0 {
				routingResult.Mark = c.soMarkFromDae
			}
			c.recordUploadTraffic(int64(len(data)))
			req := &udpRequest{
				realSrc:        realSrc,
				realDst:        realDst,
				src:            src,
				lConn:          lConn,
				routingResult:  routingResult,
				uploadRecord:   c.runtimeUploadRecorder(),
				downloadRecord: c.runtimeDownloadRecorder(),
			}
			dnsController := c.ActiveDnsController()
			if dnsController == nil {
				return fmt.Errorf("dns controller is not available")
			}
			if err := dnsController.Handle_(c.dnsRequestContext(c.ctx, dnsController), dnsMessage, req); err != nil {
				if stderrors.Is(err, ErrDNSQueryConcurrencyLimitExceeded) {
					return nil
				}
				// For DNS fast path, never leave client waiting on internal errors.
				// Respond with SERVFAIL so resolver can retry/fallback promptly.
				if sendErr := dnsController.sendDnsErrorResponse_(dnsMessage, dnsmessage.RcodeServerFailure, "ServeFail (dns fast path)", req, nil); sendErr != nil {
					return stderrors.Join(err, sendErr)
				}
				if c.log.IsLevelEnabled(logrus.DebugLevel) {
					c.log.WithError(err).Debug("DNS fast path failed; SERVFAIL sent")
				}
				return nil
			}
			return nil
		}
		// Not a valid DNS packet (port 53 but not DNS format) - fall through to normal UDP path
	}

	var ue *UdpEndpoint
	var ueExists bool
	var replayPackets []pool.PB
	defer func() {
		for _, pkt := range replayPackets {
			pkt.Put()
		}
	}()

	// Determine the correct endpoint key for initial lookup based on flow classification.
	// This avoids double sync.Map lookups by pre-selecting the appropriate key:
	// - Symmetric NAT (Src+Dst) for confirmed QUIC/sniffing sessions on sniff-eligible UDP
	// - Full-Cone NAT (Src-only) for other UDP traffic
	isQuicInitial := flowDecision.IsQuicInitial
	var quicSnifferKey PacketSnifferKey
	failedQuicDcidKnown := false
	if isQuicInitial {
		quicSnifferKey = flowDecision.PacketSnifferKey()
		failedQuicDcidKnown = IsQuicDcidFailedAt(quicSnifferKey, now)
	}
	ueKey = flowDecision.EndpointKeyForInitialLookupWithScope(routeScope, forceSymmetricKey)
	ue, ueExists = DefaultUdpEndpointPool.Get(ueKey)
	if !ueExists && !forceSymmetricKey {
		if ueKey.Dst.Port() != 0 {
			// Sniff-eligible UDP can re-enter here with a symmetric lookup even
			// though the live session is already tracked under the cheaper
			// src-only key. Reuse that exact src-only endpoint when it targets
			// the same remote, otherwise we fork a second UdpEndpoint for the
			// same 4-tuple and start another read loop.
			srcOnlyKey := flowDecision.FullConeNatEndpointKeyWithScope(routeScope)
			if srcOnlyKey != ueKey {
				if candidate, ok := DefaultUdpEndpointPool.Get(srcOnlyKey); ok && candidate.DialTarget == realDst.String() {
					ueKey = srcOnlyKey
					ue = candidate
					ueExists = true
				}
			}
		} else {
			// The reverse race also exists: a sniff-eligible packet may have
			// already created a symmetric endpoint before a sibling ordinary
			// packet reaches here. Probe that exact symmetric key before
			// creating a new src-only endpoint so the visible UDP flow stays
			// single-instanced.
			symmetricKey := flowDecision.SymmetricNatEndpointKeyWithScope(routeScope)
			if symmetricKey != ueKey {
				if candidate, ok := DefaultUdpEndpointPool.Get(symmetricKey); ok && candidate.DialTarget == realDst.String() {
					ueKey = symmetricKey
					ue = candidate
					ueExists = true
				}
			}
		}
	}
	if !ueExists {
		if fallbackKey, ok := flowDecision.InitialLookupFallbackKeyWithScope(routeScope, forceSymmetricKey); ok {
			ueKey = fallbackKey
			ue, ueExists = DefaultUdpEndpointPool.Get(ueKey)
		}
	}
	if ueExists {
		switch {
		case ue.SniffedDomain == "" && isQuicInitial:
			snifferObserved := false
			snifferChanged := false
			// Same-flow QUIC packets are serialized by ordered ingress, so a quick
			// flow-family presence check safely avoids the expensive global scan
			// when no active sniffing state exists for this 4-tuple family.
			if DefaultPacketSnifferSessionMgr.HasFlowFamilySession(quicSnifferKey) {
				snifferObserved, snifferChanged = DefaultPacketSnifferSessionMgr.ObserveFlowFamilyQuicInitial(quicSnifferKey, data)
			}

			// With sniffing restricted to explicit QUIC ports, the only remaining
			// reset guard we need is "same QUIC Initial must not retrigger reset".
			// Keep reuse when the active sniffer family still matches the same
			// connection ID, and only tear down the probing endpoint when the
			// current Initial conflicts with an already-observed QUIC session.
			shouldResetForQuicInitial := quicSnifferKey.HasCacheableDcid() && snifferChanged
			if shouldResetForQuicInitial {
				removedSniffers := DefaultPacketSnifferSessionMgr.RemoveFlowFamilySessions(quicSnifferKey)
				if c.log.IsLevelEnabled(logrus.DebugLevel) {
					c.log.WithFields(logrus.Fields{
						"src":              realSrc,
						"dst":              realDst,
						"failed_dcid":      failedQuicDcidKnown,
						"sniffer_observed": snifferObserved,
						"sniffers_removed": removedSniffers,
					}).Debug("Removed trapped domain-less UdpEndpoint for new QUIC Initial packet after flow-family mismatch")
				}
				_ = DefaultUdpEndpointPool.Remove(ueKey, ue)
				ue = nil
				ueExists = false
				break
			}
			if !c.checkUdpEndpointHealth(ue, ueKey, false) {
				ue = nil
				ueExists = false
			}
		case ue.SniffedDomain != "":
			// It is quic ...
			// Fast path.
			domain = ue.SniffedDomain
			dialTarget := realDst.String()

			if !c.checkUdpEndpointHealth(ue, ueKey, true) {
				ue = nil
				ueExists = false
			} else {
				// Update NAT timeout for QUIC connections to ensure proper timeout
				// based on the actual forwarding state (QUIC needs longer timeout)
				ue.UpdateNatTimeout(QuicNatTimeout)

				if c.log.IsLevelEnabled(logrus.TraceLevel) {
					fields := logrus.Fields{
						"network":  "udp(fp)",
						"outbound": ue.Outbound.Name,
						"policy":   ue.Outbound.GetSelectionPolicy(),
						"dialer":   ue.Dialer.Property().Name,
						"sniffed":  domain,
						"ip":       RefineAddrPortToShow(realDst),
						"pid":      routingResult.Pid,
						"dscp":     routingResult.Dscp,
						"pname":    ProcessName2String(routingResult.Pname[:]),
						"mac":      Mac2String(routingResult.Mac[:]),
					}
					c.log.WithFields(fields).Tracef("%v <-> %v", RefineSourceToShow(realSrc, realDst.Addr()), dialTarget)
				}

				ue.TrackUdpConnStateTuplePair(realSrc, realDst)
				_, err = ue.WriteTo(data, dialTarget)
				if err == nil {
					c.recordUploadTraffic(int64(len(data)))
					if lifecycle, ok := newUdpSessionLifecycleContext(ue, ""); ok {
						lifecycle.reportTrafficSuccess()
					}
					return nil
				}
				if c.log.IsLevelEnabled(logrus.DebugLevel) {
					c.log.WithFields(logrus.Fields{
						"to":      realDst.String(),
						"domain":  domain,
						"pid":     routingResult.Pid,
						"dscp":    routingResult.Dscp,
						"pname":   ProcessName2String(routingResult.Pname[:]),
						"mac":     Mac2String(routingResult.Mac[:]),
						"from":    realSrc.String(),
						"network": "udp(fp)",
						"err":     err.Error(),
					}).Debugln("Failed to write UDP fast-path packet. Remove stale endpoint and rebuild.")
				}
				if c.shouldPenalizeUdpEndpointWriteError(err) {
					if lifecycle, ok := newUdpSessionLifecycleContext(ue, ""); ok {
						lifecycle.reportUnavailable(fmt.Errorf("udp endpoint write failed: %w", err))
					}
					excludedDialer = ue.Dialer
				}
				_ = DefaultUdpEndpointPool.Remove(ueKey, ue)
				ue = nil
				ueExists = false
			}
		default:
			// Non-fast-path existing endpoint. Check health.
			if !c.checkUdpEndpointHealth(ue, ueKey, false) {
				ue = nil
				ueExists = false
			}
		}
	}

	// To keep consistency with kernel program, we only sniff DNS request sent to 53.
	var natTimeout time.Duration
	if domain == "" && !skipSniffing && !ueExists {
		// Fast path: only sniff-eligible QUIC Initial packets should enter sniffing.
		// All other UDP traffic should be forwarded immediately without blocking.
		if !isQuicInitial {
			// Not a QUIC Initial packet - skip sniffing entirely.
			// Even if there's an existing sniffer session, non-QUIC packets
			// should not be delayed by the sniffing process.
			goto afterSniffing
		}

		// Create sniffer key with DCID for QUIC connections.
		// Each DCID is like a different bus - passengers (packets) wait
		// for their specific bus to depart (complete sniffing).
		// Check if this DCID has failed sniffing before.
		// Failed DCIDs bypass sniffing entirely and use IP routing directly.
		// This prevents blocking when a previous sniffing attempt timed out.
		key := quicSnifferKey
		if failedQuicDcidKnown {
			goto afterSniffing
		}

		// Get or create sniffer for this DCID.
		_sniffer, _ := DefaultPacketSnifferSessionMgr.GetOrCreate(key, nil)
		_sniffer.Mu.Lock()
		sniffer := DefaultPacketSnifferSessionMgr.Get(key)
		if _sniffer == sniffer {

			// Check if we've hit the bypass threshold for this DCID.
			// After consecutive failures, fall back to IP routing to avoid
			// indefinitely blocking QUIC connections waiting for sniffing completion.
			if sniffer.ShouldBypassSniff(now) {
				// Mark this DCID as failed - subsequent packets will bypass sniffing.
				MarkQuicDcidFailed(key, quicDcidFailureReasonSoftBypass)
				sniffer.Mu.Unlock()
				goto afterSniffing
			}

			// Safe sniffing: wrap in a function to allow recover() from potential
			// sniffer panics (e.g., malformed packets or internal logic errors).
			func() {
				defer func() {
					if r := recover(); r != nil {
						if c.log.IsLevelEnabled(logrus.ErrorLevel) {
							c.log.WithFields(logrus.Fields{
								"src":   realSrc,
								"dst":   realDst,
								"panic": r,
							}).Error("UDP sniffing panicked; bypassing sniffing for this DCID")
						}
						MarkQuicDcidFailed(key, quicDcidFailureReasonPanic)
						sniffer.Mu.Unlock()
					}
				}()

				_, _ = sniffer.ObserveQuicInitial(data)
				sniffer.AppendData(data)
				domain, err = sniffer.SniffUdp()
				if err != nil {
					// Check for decrypt failures (malformed packets).
					// If decryption repeatedly fails, the packets are not valid QUIC
					// and retrying is pointless. Give up quickly.
					if stderrors.Is(err, sniffing.ErrNotApplicable) {
						sniffer.consecutiveDecryptFailures++
						if sniffer.consecutiveDecryptFailures >= consecutiveDecryptFailuresThreshold {
							// Too many decrypt failures - mark DCID as failed.
							if c.log.IsLevelEnabled(logrus.DebugLevel) {
								c.log.WithFields(logrus.Fields{
									"src":      realSrc.String(),
									"dst":      realDst.String(),
									"failures": sniffer.consecutiveDecryptFailures,
								}).Debug("QUIC decrypt failed repeatedly, marking DCID as failed")
							}
							MarkQuicDcidFailed(key, quicDcidFailureReasonDecryptFailure)
							sniffer.Mu.Unlock()
							return
						}
					} else {
						// Reset decrypt failure counter on non-decrypt errors.
						sniffer.consecutiveDecryptFailures = 0
					}

					// Log unexpected errors for debugging but don't drop packet.
					if !sniffing.IsSniffingError(err) {
						if logrus.IsLevelEnabled(logrus.DebugLevel) {
							logrus.WithError(err).
								WithField("from", realSrc).
								WithField("to", realDst).
								Debug("UDP sniffing encountered unexpected error but continue processing")
						}
					}
				}

				if sniffer.NeedMore() {
					// We don't record No SNI streak for NeedMore because handshakes naturally span multiple packets.
					sniffer.Mu.Unlock()
					return
				}

				if (err != nil && !stderrors.Is(err, sniffing.ErrNeedMore)) || domain == "" {
					sniffer.RecordSniffNoSni(now)
				} else if domain != "" {
					sniffer.RecordSniffSuccess()
				}

				if err != nil {
					if logrus.IsLevelEnabled(logrus.TraceLevel) {
						logrus.WithError(err).
							WithField("from", realSrc).
							WithField("to", realDst).
							Trace("sniffUdp")
					}
				}

				// Flush previously buffered packets on the same endpoint path before the
				// current packet so QUIC sniff completion preserves original ingress order.
				toReplay := sniffer.Data()[1 : len(sniffer.Data())-1] // Skip the first empty and the last (self).
				if len(toReplay) > 0 {
					replayPackets = make([]pool.PB, 0, len(toReplay))
					for _, d := range toReplay {
						dCopy := pool.Get(len(d))
						copy(dCopy, d)
						replayPackets = append(replayPackets, dCopy)
					}
				}
				sniffer.CompactPacketState()
				sniffer.Mu.Unlock()
			}()
			if sniffer.NeedMore() {
				return nil
			}

		} else {
			_sniffer.Mu.Unlock()
			// sniffer may be nil.
		}
	}

afterSniffing:
	if routingResult.Mark == 0 {
		routingResult.Mark = c.soMarkFromDae
	}
	// Dial and send.
	// TODO: Rewritten domain should not use full-cone (such as VMess Packet Addr).
	// 		Maybe we should set up a mapping for UDP: Dialer + Target Domain => Remote Resolved IP.
	//		However, games may not use QUIC for communication, thus we cannot use domain to dial, which is fine.

	// Get udp endpoint.
	// foundUeKey captures the endpoint key that the initial Get succeeded with.
	// When the target key at dial time equals foundUeKey, we can reuse ue directly
	// and skip the redundant GetOrCreate sync.Map lookup (common path: non-QUIC
	// existing endpoint, zero domain upgrade).
	foundUeKey := ueKey
	payloads := make([][]byte, 0, len(replayPackets)+1)
	for _, pkt := range replayPackets {
		payloads = append(payloads, pkt)
	}
	payloads = append(payloads, data)
	packetIndex := 0
	retry := 0
	var isNew bool
	networkType := &dialer.NetworkType{
		L4Proto:         consts.L4ProtoStr_UDP,
		IpVersion:       consts.IpVersionFromAddr(realDst.Addr()),
		IsDns:           false,
		UdpHealthDomain: dialer.UdpHealthDomainData,
	}
	// Keep UDP target pinned to original destination IP to avoid QUIC session issues.
	dialTarget := realDst.String()
getNew:
	if retry > MaxRetry {
		c.log.WithFields(logrus.Fields{
			"src":     RefineSourceToShow(realSrc, realDst.Addr()),
			"network": networkType.String(),
			"dialer":  ue.Dialer.Property().Name,
			"retry":   retry,
		}).Warnln("Touch max retry limit.")
		return fmt.Errorf("touch max retry limit")
	}

	// Determine NAT timeout based on connection type
	natTimeout = flowDecision.NatTimeoutForDial(domain)

	// Allocate symmetric endpoints only for confirmed QUIC state. If the initial
	// lookup found an existing symmetric endpoint via the sniff-eligible UDP
	// path, keep using that existing entry instead of silently forking a new
	// src-only one.
	ueKey = flowDecision.EndpointKeyForDialWithScope(domain, routeScope, forceSymmetricKey)
	if ueExists && foundUeKey.Dst.Port() != 0 && ueKey.Dst.Port() == 0 {
		ueKey = foundUeKey
		natTimeout = ue.natTimeout()
	}
	if ueExists &&
		foundUeKey.Dst.Port() == 0 &&
		ueKey.Dst.Port() != 0 &&
		domain == "" &&
		ue.SniffedDomain == "" &&
		ue.DialTarget == dialTarget {
		ueKey = foundUeKey
		natTimeout = ue.natTimeout()
	}

	// Fast path: reuse the endpoint already loaded by the initial Get when the
	// target key is unchanged (non-QUIC existing flow, no domain upgrade).
	// On any retry the endpoint was removed, so we always call GetOrCreate.
	if retry == 0 && ueExists && ueKey == foundUeKey {
		// Update NAT timeout based on current forwarding state
		// This allows the timeout to adapt to changes (e.g., domain sniffed, policy change)
		ue.UpdateNatTimeout(natTimeout)
		isNew = false
	} else {
		ue, isNew, err = DefaultUdpEndpointPool.GetOrCreate(ueKey, &UdpEndpointOptions{
			Ctx: c.ctx,
			// Handler handles response packets and send it to the client.
			Handler: func(ue *UdpEndpoint, data []byte, from netip.AddrPort) (err error) {
				return forwardUdpEndpointReplyToClient(c.log, ue, data, from, realSrc, nil, c.runtimeDownloadRecorder())
			},
			NatTimeout:     natTimeout,
			ConnStateOwner: c.core,
			DrainTracker:   c.drainTracker,
			Log:            c.log,
			NowNano:        nowNano,
			GetDialOption: func(ctx context.Context) (option *DialOption, err error) {
				dialParam := &proxyDialParam{
					Outbound:    consts.OutboundIndex(routingResult.Outbound),
					Domain:      domain,
					Mac:         routingResult.Mac,
					Dscp:        routingResult.Dscp,
					ProcessName: routingResult.Pname,
					Src:         realSrc,
					Dest:        realDst,
					Mark:        routingResult.Mark,
					Network:     "udp",
					Excluded:    excludedDialer,
				}

				res, err := c.chooseProxyDialer(ctx, dialParam)
				if err != nil {
					if res != nil && res.Outbound != nil && stderrors.Is(err, ob.ErrNoAliveDialer) {
						res.Outbound.HandleNoAliveDialer(
							res.OrigNetworkType,
							res.SelectionNetworkTypeObj,
							realSrc,
							realDst,
							domain,
							res.IsDialIp,
						)
						return nil, err
					}
					return nil, err
				}
				if shouldRejectNewUdpDialSelection(res) {
					if res.Outbound != nil {
						res.Outbound.HandleNoAliveDialer(
							res.OrigNetworkType,
							res.SelectionNetworkTypeObj,
							realSrc,
							realDst,
							domain,
							res.IsDialIp,
						)
					}
					return nil, ob.ErrNoAliveDialer
				}

				return &DialOption{
					// Keep fixed-IP target even if chooseProxyDialer selected a domain target.
					Target:        dialTarget,
					Dialer:        res.Dialer,
					Outbound:      res.Outbound,
					Network:       res.Network,
					NetworkType:   res.SelectionNetworkTypeObj,
					SniffedDomain: res.SniffedDomain,
					Excluded:      excludedDialer,
					NowNano:       nowNano,
				}, nil
			},
		})
		if err != nil {
			if stderrors.Is(err, ob.ErrNoAliveDialer) || stderrors.Is(err, ErrEndpointFailed) {
				// Already emitted a rate-limited diagnostic log above, or hit negative cache.
				return nil
			}
			if c.allowConnectionErrorLog(now) {
				return fmt.Errorf("failed to GetOrCreate: %w", err)
			}
			return nil
		}
	}

	// If GetOrCreate reused an existing endpoint on the slow path, re-check the
	// exact endpoint network type before writing. This keeps the slow path aligned
	// with the fast-path health checks and exact per-family invalidation.
	if !isNew && !c.checkUdpEndpointHealth(ue, ueKey, false) {
		// Exclude the dead dialer to force selection of a different one on retry.
		excludedDialer = ue.Dialer
		retry++
		goto getNew
	}
	if domain == "" {
		// It is used for showing.
		domain = ue.SniffedDomain
	}
	ue.TrackUdpConnStateTuplePair(realSrc, realDst)

	for packetIndex < len(payloads) {
		_, err = ue.WriteTo(payloads[packetIndex], dialTarget)
		if err != nil {
			if c.log.IsLevelEnabled(logrus.DebugLevel) {
				c.log.WithFields(logrus.Fields{
					"to":      realDst.String(),
					"domain":  domain,
					"pid":     routingResult.Pid,
					"dscp":    routingResult.Dscp,
					"pname":   ProcessName2String(routingResult.Pname[:]),
					"mac":     Mac2String(routingResult.Mac[:]),
					"from":    realSrc.String(),
					"network": networkType.StringWithoutDns(),
					"err":     err.Error(),
					"retry":   retry,
					"packet":  packetIndex,
				}).Debugln("Failed to write UDP packet request. Try to remove old UDP endpoint and retry.")
			}
			if c.shouldPenalizeUdpEndpointWriteError(err) {
				if lifecycle, ok := newUdpSessionLifecycleContext(ue, ""); ok {
					lifecycle.reportUnavailable(fmt.Errorf("udp endpoint write failed: %w", err))
				}
			}
			// Ensure the failed dialer is excluded in the immediate retry if it was a real failure.
			// For normal closures, we still remove the endpoint but don't penalize the dialer.
			if c.shouldPenalizeUdpEndpointWriteError(err) {
				excludedDialer = ue.Dialer
			}
			_ = DefaultUdpEndpointPool.Remove(ueKey, ue)
			retry++
			goto getNew
		}
		c.recordUploadTraffic(int64(len(payloads[packetIndex])))
		packetIndex++
	}
	if lifecycle, ok := newUdpSessionLifecycleContext(ue, ""); ok {
		lifecycle.reportTrafficSuccess()
	}

	// Print log.
	// Only print routing for new connection to avoid the log exploded (Quic and BT).
	if (isNew && c.log.IsLevelEnabled(logrus.InfoLevel)) || c.log.IsLevelEnabled(logrus.DebugLevel) {
		entry := c.log.WithFields(logrus.Fields{
			"network":  networkType.StringWithoutDns(),
			"outbound": ue.Outbound.Name,
			"policy":   ue.Outbound.GetSelectionPolicy(),
			"dialer":   ue.Dialer.Property().Name,
			"sniffed":  domain,
			"ip":       RefineAddrPortToShow(realDst),
			"pid":      routingResult.Pid,
			"dscp":     routingResult.Dscp,
			"pname":    ProcessName2String(routingResult.Pname[:]),
			"mac":      Mac2String(routingResult.Mac[:]),
		})
		// Build entry once; select level without a second WithFields allocation.
		logger := entry.Infof
		if !isNew && c.log.IsLevelEnabled(logrus.DebugLevel) {
			logger = entry.Debugf
		}
		logger("%v <-> %v", RefineSourceToShow(realSrc, realDst.Addr()), dialTarget)
	}

	return nil
}

func (c *ControlPlane) shouldPenalizeUdpEndpointWriteError(err error) bool {
	return err != nil && !errors.IsUDPEndpointNormalClose(err)
}
