punchnet-macos/Tun/Policy/PolicyRuntime.swift
2026-05-25 21:54:20 +08:00

86 lines
2.7 KiB
Swift

//
// 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 self.isAllowedBySession(ipPacket: ipPacket) {
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 isAllowedBySession(ipPacket: IPPacketView) -> Bool {
guard let reverseFlowSession = ipPacket.flowSession()?.reverse() else {
return false
}
switch ipPacket.transportPacket {
case .tcp(_, _, let flags):
return self.flowSessionTable.allowInboundTCP(reverseFlowSession, flags: flags)
case .udp(let srcPort, _, _):
return self.flowSessionTable.allowInboundUDP(reverseFlowSession, isDNS: srcPort == 53)
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"
}
}
}