punchnet-macos/Tun/Punchnet/ArpServer.swift
2026-03-27 11:39:44 +08:00

106 lines
2.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ArpServer.swift
// sdlan
// 1. ipmac
// 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()
}
}