106 lines
2.7 KiB
Swift
106 lines
2.7 KiB
Swift
//
|
||
// ArpServer.swift
|
||
// sdlan
|
||
// 1. 通过ip地址查找mac地址
|
||
// 2. 要限制单位时间内,同一个ip的查询
|
||
// Created by 安礼成 on 2025/7/14.
|
||
//
|
||
import Foundation
|
||
import Darwin
|
||
|
||
actor ArpServer {
|
||
// 增加缓存时间逻辑
|
||
struct ArpEntry {
|
||
var mac: Data
|
||
var expireTime: TimeInterval
|
||
}
|
||
|
||
private var coolingDown: [UInt32: Date] = [:]
|
||
|
||
private var known_macs: [UInt32: ArpEntry] = [:]
|
||
private let arpTTL: TimeInterval
|
||
|
||
private var cleanupTask: Task<Void, Never>?
|
||
|
||
init(arpTTL: TimeInterval = 300) {
|
||
self.arpTTL = arpTTL
|
||
}
|
||
|
||
func start() {
|
||
guard self.cleanupTask == nil else {
|
||
return
|
||
}
|
||
|
||
self.cleanupTask = Task {
|
||
while !Task.isCancelled {
|
||
try? await Task.sleep(for: .seconds(1))
|
||
self.cleanup()
|
||
}
|
||
}
|
||
}
|
||
|
||
func query(ip: UInt32) -> Data? {
|
||
guard let entry = known_macs[ip] else {
|
||
return nil
|
||
}
|
||
|
||
if entry.expireTime < Date().timeIntervalSince1970 {
|
||
known_macs.removeValue(forKey: ip)
|
||
return nil
|
||
}
|
||
|
||
return entry.mac
|
||
}
|
||
|
||
func append(ip: UInt32, mac: Data) {
|
||
let expireAt = Date().timeIntervalSince1970 + arpTTL
|
||
self.known_macs[ip] = ArpEntry(mac: mac, expireTime: expireAt)
|
||
}
|
||
|
||
func remove(ip: UInt32) {
|
||
self.known_macs.removeValue(forKey: ip)
|
||
}
|
||
|
||
func dropMacs(macs: [Data]) {
|
||
self.known_macs = self.known_macs.filter { !macs.contains($0.value.mac) }
|
||
}
|
||
|
||
func clear() {
|
||
self.known_macs = [:]
|
||
}
|
||
|
||
func arpRequest(targetIp: UInt32, use quicClient: SDLQUICClient?) throws {
|
||
guard let quicClient, self.coolingDown[targetIp] == nil else {
|
||
return
|
||
}
|
||
|
||
// 单位时间内指允许提交一次
|
||
self.coolingDown[targetIp] = Date().addingTimeInterval(3)
|
||
|
||
// 进行arp查询
|
||
var arpRequest = SDLArpRequest()
|
||
arpRequest.targetIp = targetIp
|
||
|
||
quicClient.send(type: .arpRequest, data: try arpRequest.serializedData())
|
||
}
|
||
|
||
func handleArpResponse(arpResponse: SDLArpResponse) {
|
||
let targetIp = arpResponse.targetIp
|
||
let targetMac = arpResponse.targetMac
|
||
if !targetMac.isEmpty {
|
||
let expireAt = Date().timeIntervalSince1970 + arpTTL
|
||
self.known_macs[targetIp] = ArpEntry(mac: targetMac, expireTime: expireAt)
|
||
}
|
||
}
|
||
|
||
private func cleanup() {
|
||
let now = Date()
|
||
self.coolingDown = self.coolingDown.filter { $0.value > now }
|
||
}
|
||
|
||
deinit {
|
||
self.cleanupTask?.cancel()
|
||
}
|
||
|
||
}
|