diff --git a/Tun/Inbound/PacketInboundActor.swift b/Tun/Inbound/PacketInboundActor.swift index e061a30..59a7825 100644 --- a/Tun/Inbound/PacketInboundActor.swift +++ b/Tun/Inbound/PacketInboundActor.swift @@ -69,11 +69,13 @@ actor PacketInboundActor { let packet = NEPacket(data: packetData, protocolFamily: 2) self.provider.packetFlow.writePacketObjects([packet]) SDLLogger.log("[PacketInboundActor] hole identity: \(identityID), allow, data count: \(packetData.count)", for: .trace) - case .requestPolicy(let srcIdentityID): - SDLLogger.log("[PacketInboundActor] not found identity: \(srcIdentityID) ruleMap", for: .debug) - if let queryData = await self.policyService.makePolicyRequest(srcIdentityID: srcIdentityID) { + case .requestPolicy(let context): + SDLLogger.log("[PacketInboundActor] policy miss, \(context.logDescription)", for: .debug) + if let queryData = await self.policyService.makePolicyRequest(srcIdentityID: context.srcIdentityID) { await self.superServiceProxy.send(type: .policyRequest, data: queryData) } + case .dropByPolicy(let context): + SDLLogger.log("[PacketInboundActor] policy denied, \(context.logDescription)", for: .trace) case .none: () } diff --git a/Tun/Inbound/PacketInboundProcessor.swift b/Tun/Inbound/PacketInboundProcessor.swift index a9072a9..1d90eaf 100644 --- a/Tun/Inbound/PacketInboundProcessor.swift +++ b/Tun/Inbound/PacketInboundProcessor.swift @@ -8,12 +8,27 @@ import Foundation final class PacketInboundProcessor { + struct PolicyPacketContext { + let srcIdentityID: UInt32 + let proto: UInt8 + let srcIP: UInt32 + let dstIP: UInt32 + let srcPort: UInt16? + let dstPort: UInt16? + + var logDescription: String { + let srcPort = self.srcPort.map(String.init) ?? "-" + let dstPort = self.dstPort.map(String.init) ?? "-" + return "srcIdentityID: \(self.srcIdentityID), proto: \(self.proto), srcIP: \(SDLUtil.int32ToIp(self.srcIP)), dstIP: \(SDLUtil.int32ToIp(self.dstIP)), srcPort: \(srcPort), dstPort: \(dstPort)" + } + } enum ProcessingAction { case sendARPReply(dstMac: Data, data: Data) case appendARP(ip: UInt32, mac: Data) case writeToTun(packetData: Data, identityID: UInt32) - case requestPolicy(srcIdentityID: UInt32) + case requestPolicy(PolicyPacketContext) + case dropByPolicy(PolicyPacketContext) case none } @@ -93,16 +108,43 @@ final class PacketInboundProcessor { return .init(inboundBytes: inboundBytes, action: .none) } - if self.policyRuntime.allowsInbound(srcIdentityID: identityID, ipPacket: ipPacket) { + switch self.policyRuntime.evaluateInbound(srcIdentityID: identityID, ipPacket: ipPacket) { + case .allow: return .init( inboundBytes: inboundBytes, action: .writeToTun(packetData: ipPacket.data, identityID: identityID) ) + case .deny: + return .init( + inboundBytes: inboundBytes, + action: .dropByPolicy(self.makePolicyPacketContext(identityID: identityID, ipPacket: ipPacket)) + ) + case .missingPolicy: + return .init( + inboundBytes: inboundBytes, + action: .requestPolicy(self.makePolicyPacketContext(identityID: identityID, ipPacket: ipPacket)) + ) } - - return .init( - inboundBytes: inboundBytes, - action: .requestPolicy(srcIdentityID: identityID) + } + + private func makePolicyPacketContext(identityID: UInt32, ipPacket: IPPacket) -> PolicyPacketContext { + let ports: (UInt16?, UInt16?) + switch ipPacket.transportPacket { + case .tcp(let tcpPacket): + ports = (tcpPacket.header.srcPort, tcpPacket.header.dstPort) + case .udp(let udpPacket): + ports = (udpPacket.srcPort, udpPacket.dstPort) + default: + ports = (nil, nil) + } + + return PolicyPacketContext( + srcIdentityID: identityID, + proto: ipPacket.header.proto, + srcIP: ipPacket.header.source, + dstIP: ipPacket.header.destination, + srcPort: ports.0, + dstPort: ports.1 ) } } diff --git a/Tun/Policy/PolicyRuleStore.swift b/Tun/Policy/PolicyRuleStore.swift index f96d245..adc8661 100644 --- a/Tun/Policy/PolicyRuleStore.swift +++ b/Tun/Policy/PolicyRuleStore.swift @@ -36,6 +36,10 @@ actor PolicyRuleStore { } func makePolicyRequest(srcIdentityId: UInt32, dstIdentityId: UInt32) -> Data? { + guard self.ruleMapByIdentity[srcIdentityId] == nil else { + return nil + } + guard !coolingDown.contains(srcIdentityId) else { return nil } @@ -75,6 +79,7 @@ actor PolicyRuleStore { ruleMap[proto, default: [:]][port] = true } self.ruleMapByIdentity[id] = PolicyRuleMap(version: version, ruleMap: ruleMap) + SDLLogger.log("[PolicyRuleStore] apply policy response, srcIdentityID: \(id), version: \(version), rulesCount: \(ruleMap.reduce(0) { $0 + $1.value.count })", for: .debug) // 发布新的快照信息 let snapshot = compileSnapshot() diff --git a/Tun/Policy/PolicyRuntime.swift b/Tun/Policy/PolicyRuntime.swift index d6c1055..06a162a 100644 --- a/Tun/Policy/PolicyRuntime.swift +++ b/Tun/Policy/PolicyRuntime.swift @@ -8,6 +8,12 @@ import Foundation struct PolicyRuntime: @unchecked Sendable { + enum InboundDecision { + case allow + case deny + case missingPolicy + } + private let policyRuleSnapshot: PolicyRuleSnapshot private let flowSessionTable: FlowSessionTable @@ -16,27 +22,32 @@ struct PolicyRuntime: @unchecked Sendable { self.flowSessionTable = flowSessionTable } - func allowsInbound(srcIdentityID: UInt32, ipPacket: IPPacket) -> Bool { + func evaluateInbound(srcIdentityID: UInt32, ipPacket: IPPacket) -> InboundDecision { if let reverseFlowSession = ipPacket.flowSession()?.reverse(), self.flowSessionTable.hasSession(reverseFlowSession) { self.flowSessionTable.updateSession(reverseFlowSession) - return true + return .allow } - return self.allowsByRule(srcIdentityID: srcIdentityID, ipPacket: ipPacket) + if case .icmp = ipPacket.transportPacket { + return .allow + } + + guard let ruleMap = self.policyRuleSnapshot.lookup(srcIdentityID) else { + return .missingPolicy + } + + return self.isAllowedByRule(ruleMap: ruleMap, ipPacket: ipPacket) ? .allow : .deny } - private func allowsByRule(srcIdentityID: UInt32, ipPacket: IPPacket) -> Bool { - let ruleMap = self.policyRuleSnapshot.lookup(srcIdentityID) + private func isAllowedByRule(ruleMap: PolicyRuleMap, ipPacket: IPPacket) -> Bool { let proto = ipPacket.header.proto switch ipPacket.transportPacket { case .tcp(let tcpPacket): - return ruleMap?.isAllow(proto: proto, port: tcpPacket.header.dstPort) ?? false + return ruleMap.isAllow(proto: proto, port: tcpPacket.header.dstPort) case .udp(let udpPacket): - return ruleMap?.isAllow(proto: proto, port: udpPacket.dstPort) ?? false - case .icmp: - return true + return ruleMap.isAllow(proto: proto, port: udpPacket.dstPort) default: return false }