// // PolicyRuntime.swift // Tun // // Created by Codex on 2026/5/21. // import Foundation struct PolicyRuntime: @unchecked Sendable { enum InboundDecision { case allow case deny case missingPolicy } private let policyRuleSnapshot: PolicyRuleSnapshot private let flowSessionTable: FlowSessionTable init(policyRuleSnapshot: PolicyRuleSnapshot, flowSessionTable: FlowSessionTable) { self.policyRuleSnapshot = policyRuleSnapshot self.flowSessionTable = flowSessionTable } func evaluateInbound(srcIdentityID: UInt32, ipPacket: IPPacketView) -> InboundDecision { if let reverseFlowSession = ipPacket.flowSession()?.reverse(), self.flowSessionTable.hasSession(reverseFlowSession) { self.flowSessionTable.updateSession(reverseFlowSession) SDLLogger.log("[PolicyRuntime] session hit, src_identify_id: \(srcIdentityID), allow: \(debugInfo(ipPacket: ipPacket))") return .allow } if case .icmp = ipPacket.transportPacket { SDLLogger.log("[PolicyRuntime] icmp hit, src_identify_id: \(srcIdentityID), allow: \(debugInfo(ipPacket: ipPacket))") return .allow } guard let ruleMap = self.policyRuleSnapshot.lookup(srcIdentityID) else { return .missingPolicy } let isAllowed = self.isAllowedByRule(ruleMap: ruleMap, ipPacket: ipPacket) SDLLogger.log("[PolicyRuntime] rule hit: \(isAllowed), src_identify_id: \(srcIdentityID), allow: \(debugInfo(ipPacket: ipPacket))") return isAllowed ? .allow : .deny } private func isAllowedByRule(ruleMap: PolicyRuleMap, ipPacket: IPPacketView) -> Bool { let proto = ipPacket.header.proto switch ipPacket.transportPacket { case .tcp(_, let dstPort): return ruleMap.isAllow(proto: proto, port: dstPort) case .udp(_, let dstPort, _): return ruleMap.isAllow(proto: proto, port: dstPort) default: return false } } private func debugInfo(ipPacket: IPPacketView) -> String { switch ipPacket.transportPacket { case .tcp(_, let dstPort): return "tcp: \(dstPort)" case .udp(_, let dstPort, _): return "udp: \(dstPort)" default: return "unknown" } } }