fix Snapshot

This commit is contained in:
anlicheng 2026-05-20 17:58:31 +08:00
parent 153f172daf
commit c5c06cbb1f
6 changed files with 71 additions and 7 deletions

View File

@ -19,11 +19,13 @@ actor ArpServer {
private var known_macs: [UInt32: ArpEntry] = [:]
private let arpTTL: TimeInterval
nonisolated private let snapshotPublisher: SnapshotPublisher<ArpSnapshot>
private var cleanupTask: Task<Void, Never>?
init(arpTTL: TimeInterval = 300) {
self.arpTTL = arpTTL
self.snapshotPublisher = SnapshotPublisher(initial: ArpSnapshot.empty())
}
func start() {
@ -46,6 +48,7 @@ actor ArpServer {
if entry.expireTime < Date().timeIntervalSince1970 {
known_macs.removeValue(forKey: ip)
self.publishSnapshot()
return nil
}
@ -55,19 +58,23 @@ actor ArpServer {
func append(ip: UInt32, mac: Data) {
let expireAt = Date().timeIntervalSince1970 + arpTTL
self.known_macs[ip] = ArpEntry(mac: mac, expireTime: expireAt)
self.publishSnapshot()
}
func remove(ip: UInt32) {
self.known_macs.removeValue(forKey: ip)
self.publishSnapshot()
}
func dropMacs(macs: [Data]) {
self.known_macs = self.known_macs.filter { !macs.contains($0.value.mac) }
self.publishSnapshot()
}
func clear() {
self.known_macs = [:]
self.coolingDown = [:]
self.publishSnapshot()
}
func stop() {
@ -95,12 +102,34 @@ actor ArpServer {
if !targetMac.isEmpty {
let expireAt = Date().timeIntervalSince1970 + arpTTL
self.known_macs[targetIp] = ArpEntry(mac: targetMac, expireTime: expireAt)
self.publishSnapshot()
}
}
nonisolated func snapshot() -> ArpSnapshot {
return self.snapshotPublisher.current()
}
private func cleanup() {
let now = Date()
self.coolingDown = self.coolingDown.filter { $0.value > now }
let oldCount = self.known_macs.count
self.known_macs = self.known_macs.filter { $0.value.expireTime >= now.timeIntervalSince1970 }
if self.known_macs.count != oldCount {
self.publishSnapshot()
}
}
private func publishSnapshot() {
self.snapshotPublisher.publish(self.compileSnapshot())
}
private func compileSnapshot() -> ArpSnapshot {
let now = Date().timeIntervalSince1970
let entries = self.known_macs.compactMapValues { entry in
entry.expireTime >= now ? entry.mac : nil
}
return ArpSnapshot(entries: entries)
}
deinit {

View File

@ -0,0 +1,24 @@
//
// ArpSnapshot.swift
// Tun
//
// Created by Codex on 2026/5/20.
//
import Foundation
final class ArpSnapshot: Snapshot {
private let entries: [UInt32: Data]
init(entries: [UInt32: Data]) {
self.entries = entries
}
func lookup(_ ip: UInt32) -> Data? {
return self.entries[ip]
}
static func empty() -> ArpSnapshot {
return ArpSnapshot(entries: [:])
}
}

View File

@ -195,7 +195,7 @@ actor PacketOutboundActor {
SDLLogger.log("[PacketOutboundActor] use exit_node: \(SDLUtil.int32ToIp(ip))", for: .trace)
}
if let dstMac = await self.arpServer.query(ip: ip) {
if let dstMac = self.arpServer.snapshot().lookup(ip) {
SDLLogger.log("[PacketOutboundActor] dstIp: \(SDLUtil.int32ToIp(ip)), dst_mac is: \(SDLUtil.formatMacAddress(mac: dstMac))", for: .trace)
await self.routeLayerPacket(dstMac: dstMac, type: type, data: data)
} else {

View File

@ -4,8 +4,9 @@
//
// Created by on 2026/2/5.
//
import Foundation
final class IdentitySnapshot {
final class IdentitySnapshot: Snapshot {
typealias IdentityID = UInt32
private let identityMap: [IdentityID: IdentityRuleMap]

View File

@ -0,0 +1,10 @@
//
// Snapshot.swift
// punchnet
//
// Created by on 2026/5/20.
//
protocol Snapshot: AnyObject {
}

View File

@ -6,21 +6,21 @@
//
import Atomics
final class SnapshotPublisher<IdentitySnapshot: AnyObject> {
private let atomic: ManagedAtomic<Unmanaged<IdentitySnapshot>>
final class SnapshotPublisher<S: Snapshot> {
private let atomic: ManagedAtomic<Unmanaged<S>>
init(initial snapshot: IdentitySnapshot) {
init(initial snapshot: S) {
self.atomic = ManagedAtomic(.passRetained(snapshot))
}
func publish(_ snapshot: IdentitySnapshot) {
func publish(_ snapshot: S) {
let newRef = Unmanaged.passRetained(snapshot)
let oldRef = atomic.exchange(newRef, ordering: .acquiring)
oldRef.release()
}
@inline(__always)
func current() -> IdentitySnapshot {
func current() -> S {
atomic.load(ordering: .relaxed).takeUnretainedValue()
}