punchnet-macos/Tun/Punchnet/ArpServer.swift
2026-03-10 18:06:15 +08:00

115 lines
2.5 KiB
Swift

//
// ArpServer.swift
// sdlan
//
// Created by on 2025/7/14.
//
import Foundation
import Darwin
final class ArpServer {
//
struct ArpEntry {
var mac: Data
var expireTime: TimeInterval
}
private let locker = NSLock()
private var packetId: UInt32 = 1
private var known_macs: [UInt32: ArpEntry] = [:]
private let arpTTL: TimeInterval
init(arpTTL: TimeInterval = 300) {
self.arpTTL = arpTTL
}
func query(ip: UInt32) -> Data? {
locker.lock()
defer {
locker.unlock()
}
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) {
locker.lock()
defer {
locker.unlock()
}
let expireAt = Date().timeIntervalSince1970 + arpTTL
self.known_macs[ip] = ArpEntry(mac: mac, expireTime: expireAt)
}
func remove(ip: UInt32) {
locker.lock()
defer {
locker.unlock()
}
self.known_macs.removeValue(forKey: ip)
}
func dropMacs(macs: [Data]) {
locker.lock()
defer {
locker.unlock()
}
self.known_macs = self.known_macs.filter { !macs.contains($0.value.mac) }
}
func clear() {
locker.lock()
defer {
locker.unlock()
}
self.known_macs = [:]
}
func arpRequest(targetIp: UInt32, use quicClient: SDLQUICClient?) throws {
locker.lock()
defer {
locker.unlock()
}
guard let quicClient else {
return
}
// arp
var arpRequest = SDLArpRequest()
arpRequest.targetIp = targetIp
arpRequest.pktID = self.packetId
self.packetId += 1
quicClient.send(type: .arpRequest, data: try arpRequest.serializedData())
}
func handleArpResponse(arpResponse: SDLArpResponse) {
locker.lock()
defer {
locker.unlock()
}
let targetIp = arpResponse.targetIp
let targetMac = arpResponse.targetMac
if !targetMac.isEmpty {
self.append(ip: targetIp, mac: targetMac)
}
}
}