punchnet-macos/Tun/Policy/PolicyRuleStore.swift
2026-05-21 15:15:32 +08:00

112 lines
3.8 KiB
Swift

//
// PolicyRuleStore.swift
// punchnet
// 1.
// Created by on 2026/2/5.
//
import Foundation
import NIO
actor PolicyRuleStore {
//
nonisolated private let cooldown: Duration = .seconds(5)
// identityId
private var coolingDown: Set<UInt32> = []
// , map[identityId] = version
private var versions: [UInt32: UInt32] = [:]
nonisolated private let alloctor = ByteBufferAllocator()
private let publisher: SnapshotPublisher<PolicyRuleSnapshot>
private var ruleMapByIdentity: [UInt32: PolicyRuleMap] = [:]
init(publisher: SnapshotPublisher<PolicyRuleSnapshot>) {
self.publisher = publisher
}
func makeBatchPolicyRequests(dstIdentityID: UInt32) -> [Data] {
return self.ruleMapByIdentity.keys.compactMap { identityId in
var policyRequest = SDLPolicyRequest()
policyRequest.srcIdentityID = identityId
policyRequest.dstIdentityID = dstIdentityID
policyRequest.version = self.nextVersion(identityId: identityId)
return try? policyRequest.serializedData()
}
}
func makePolicyRequest(srcIdentityId: UInt32, dstIdentityId: UInt32) -> Data? {
guard self.ruleMapByIdentity[srcIdentityId] == nil else {
return nil
}
guard !coolingDown.contains(srcIdentityId) else {
return nil
}
var policyRequest = SDLPolicyRequest()
policyRequest.srcIdentityID = srcIdentityId
policyRequest.dstIdentityID = dstIdentityId
policyRequest.version = self.nextVersion(identityId: srcIdentityId)
coolingDown.insert(srcIdentityId)
Task { [weak self] in
try? await Task.sleep(for: .seconds(5))
await self?.endCooldown(for: srcIdentityId)
}
return try? policyRequest.serializedData()
}
//
func applyPolicyResponse(_ policyResponse: SDLPolicyResponse) {
let id = policyResponse.srcIdentityID
let version = policyResponse.version
guard self.ruleMapByIdentity[id] == nil || ((self.ruleMapByIdentity[id]?.version ?? 0) < version) else {
return
}
//
var buffer = alloctor.buffer(bytes: policyResponse.rules)
var ruleMap: [UInt8: [UInt16: Bool]] = [:]
while true {
guard let proto = buffer.readInteger(endianness: .big, as: UInt8.self),
let port = buffer.readInteger(endianness: .big, as: UInt16.self) else {
break
}
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()
publisher.publish(snapshot)
}
func clear() {
self.coolingDown.removeAll()
self.versions.removeAll()
self.ruleMapByIdentity.removeAll()
self.publisher.publish(PolicyRuleSnapshot.empty())
}
private func compileSnapshot() -> PolicyRuleSnapshot {
return PolicyRuleSnapshot(ruleMapByIdentity: ruleMapByIdentity)
}
private func endCooldown(for key: UInt32) {
self.coolingDown.remove(key)
}
private func nextVersion(identityId: UInt32) -> UInt32 {
let version = self.versions[identityId, default: 1]
//
self.versions[identityId] = version + 1
return version
}
}