punchnet-macos/Tun/Punchnet/Context/PacketOutboundActor.swift

183 lines
7.5 KiB
Swift

//
// PacketOutboundActor.swift
// Tun
//
// Created by Codex on 2026/5/20.
//
import Foundation
import NetworkExtension
import NIOCore
actor PacketOutboundActor {
private let provider: NEPacketTunnelProvider
private let sessionManager: SessionManager
private let arpServer: ArpServer
private let puncherActor: SDLPuncherActor
private let policyService: PolicyService
private let superServiceProxy: SDLSuperServiceProxy
private let udpHoleServiceProxy: SDLUDPHoleServiceProxy
private let flowTracer: SDLFlowTracer
private var packetReaderService: SDLPacketReaderService?
private var networkAddress: SDLConfiguration.NetworkAddress
private var identityId: UInt32
private var exitNode: SDLConfiguration.ExitNode?
private var stunSocketAddress: SocketAddress
private var dataCipher: CCDataCipher?
private var dnsService: SDLDNSService?
init(provider: NEPacketTunnelProvider,
config: SDLConfiguration,
dataCipher: CCDataCipher?,
sessionManager: SessionManager,
arpServer: ArpServer,
puncherActor: SDLPuncherActor,
policyService: PolicyService,
superServiceProxy: SDLSuperServiceProxy,
udpHoleServiceProxy: SDLUDPHoleServiceProxy,
flowTracer: SDLFlowTracer) {
self.provider = provider
self.networkAddress = config.networkAddress
self.identityId = config.identityId
self.exitNode = config.exitNode
self.stunSocketAddress = config.stunSocketAddress
self.dataCipher = dataCipher
self.sessionManager = sessionManager
self.arpServer = arpServer
self.puncherActor = puncherActor
self.policyService = policyService
self.superServiceProxy = superServiceProxy
self.udpHoleServiceProxy = udpHoleServiceProxy
self.flowTracer = flowTracer
}
func updateRuntime(config: SDLConfiguration, dataCipher: CCDataCipher?) {
self.networkAddress = config.networkAddress
self.identityId = config.identityId
self.exitNode = config.exitNode
self.stunSocketAddress = config.stunSocketAddress
self.dataCipher = dataCipher
}
func updateDNSService(_ dnsService: SDLDNSService?) {
self.dnsService = dnsService
}
func startPacketReader() async {
if self.packetReaderService == nil {
self.packetReaderService = SDLPacketReaderService(provider: self.provider) { [weak self] event in
await self?.handlePacketReaderEvent(event)
}
}
await self.packetReaderService?.start()
}
func stop() async {
let packetReaderService = self.packetReaderService
self.packetReaderService = nil
await packetReaderService?.stop()
}
func handleTunPacket(_ packet: IPPacket) async {
let router = SDLTunPacketRouter(networkAddress: self.networkAddress, exitNode: self.exitNode)
let decision = router.route(packet: packet)
if decision.shouldTrackFlow, let flowSession = packet.flowSession() {
await self.policyService.flowSessionManager.updateSession(flowSession)
}
await self.handleTunRouteDecision(decision)
}
private func handlePacketReaderEvent(_ event: SDLPacketReaderService.Event) async {
switch event {
case .packet(let packet):
await self.handleTunPacket(packet)
}
}
func routeLayerPacket(dstMac: Data, type: LayerPacket.PacketType, data: Data) async {
let forwarder = self.makeLayerPacketForwarder()
guard let plan = try? await forwarder.makeDeliveryPlan(dstMac: dstMac, type: type, data: data) else {
return
}
switch plan {
case .superNode(let payload):
await self.sendSuperPacket(type: .data, data: payload)
case .peer(let payload, let session):
SDLLogger.log("[PacketOutboundActor] step 5 send packet by session: \(session)", for: .trace)
await self.sendPeerPacket(type: .data, data: payload, remoteAddress: session.natAddress)
self.flowTracer.inc(num: payload.count, type: .p2p)
case .superNodeAndPunch(let payload, let request):
await self.sendSuperPacket(type: .data, data: payload)
SDLLogger.log("[PacketOutboundActor] step 5 send packet by super: \(self.stunSocketAddress)", for: .trace)
self.flowTracer.inc(num: payload.count, type: .forward)
if let queryData = await self.puncherActor.makeQueryInfoRequest(request: request) {
await self.superServiceProxy.send(type: .queryInfo, data: queryData)
}
}
}
private func handleTunRouteDecision(_ decision: SDLTunPacketRouter.RouteDecision) async {
switch decision {
case .loopback(let ipPacketData):
let nePacket = NEPacket(data: ipPacketData, protocolFamily: 2)
self.provider.packetFlow.writePacketObjects([nePacket])
case .cloudDNS(let name, let ipPacketData):
SDLLogger.log("[PacketOutboundActor] get cloud dns request: \(name)")
await self.dnsService?.forward(ipPacketData: ipPacketData)
case .localDNS(let name, let payload, let tracker):
SDLLogger.log("[PacketOutboundActor] get local dns request: \(name)")
await self.dnsService?.queryLocal(tracker: tracker, dnsPayload: payload)
case .forwardToNextHop(let ip, let type, let data, let kind):
await self.forwardPacketToNextHop(ip: ip, type: type, data: data, kind: kind)
case .drop(let reason):
SDLLogger.log("[PacketOutboundActor] drop tun packet, reason: \(reason.rawValue)", for: .trace)
}
}
private func forwardPacketToNextHop(ip: UInt32, type: LayerPacket.PacketType, data: Data, kind: SDLTunPacketRouter.ForwardKind) async {
switch kind {
case .sameNetwork:
SDLLogger.log("[PacketOutboundActor] dstIp: \(SDLUtil.int32ToIp(ip)) same network", for: .trace)
case .exitNode, .dnsExitNode:
SDLLogger.log("[PacketOutboundActor] use exit_node: \(SDLUtil.int32ToIp(ip))", for: .trace)
}
if let dstMac = await self.arpServer.query(ip: 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 {
SDLLogger.log("[PacketOutboundActor] dstIp: \(SDLUtil.int32ToIp(ip)) arp query not found, broadcast", for: .trace)
if let arpRequest = try? await self.arpServer.makeArpRequest(targetIp: ip) {
await self.superServiceProxy.send(type: .arpRequest, data: arpRequest)
}
}
}
private func makeLayerPacketForwarder() -> SDLLayerPacketForwarder {
return .init(
networkAddress: self.networkAddress,
identityID: self.identityId,
dataCipher: self.dataCipher,
sessionManager: self.sessionManager
)
}
private func sendSuperPacket(type: SDLPacketType, data: Data) async {
await self.sendPacket(type: type, data: data, remoteAddress: self.stunSocketAddress)
}
private func sendPeerPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async {
await self.sendPacket(type: type, data: data, remoteAddress: remoteAddress)
}
private func sendPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async {
await self.udpHoleServiceProxy.send(type: type, data: data, remoteAddress: remoteAddress)
}
}