From ffb21f52c7796c774e92311e58e7d4e650263669 Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Wed, 20 May 2026 15:41:09 +0800 Subject: [PATCH] add PacketOutboundActor --- .../Context/PacketOutboundActor.swift | 158 ++++++++++++++++++ Tun/Punchnet/Context/SDLContextActor.swift | 154 +++++------------ 2 files changed, 200 insertions(+), 112 deletions(-) create mode 100644 Tun/Punchnet/Context/PacketOutboundActor.swift diff --git a/Tun/Punchnet/Context/PacketOutboundActor.swift b/Tun/Punchnet/Context/PacketOutboundActor.swift new file mode 100644 index 0000000..a7d2033 --- /dev/null +++ b/Tun/Punchnet/Context/PacketOutboundActor.swift @@ -0,0 +1,158 @@ +// +// 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 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 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) + } + + 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) + } +} diff --git a/Tun/Punchnet/Context/SDLContextActor.swift b/Tun/Punchnet/Context/SDLContextActor.swift index 1281359..0512a75 100644 --- a/Tun/Punchnet/Context/SDLContextActor.swift +++ b/Tun/Punchnet/Context/SDLContextActor.swift @@ -37,11 +37,6 @@ func startMonitorTask(name: String, _ body: @escaping () async throws -> Void, r } } -// 转换成ip地址 -private func asIpAddress(_ ipNum: UInt32) -> String { - return SDLUtil.int32ToIp(ipNum) -} - enum SDLContextError: Error { case udpHoleClosed @@ -70,8 +65,9 @@ actor SDLContextActor { private var dnsService: SDLDNSService? private var packetReaderService: SDLPacketReaderService? - private let superServiceProxy = SDLSuperServiceProxy() - private let udpHoleServiceProxy = SDLUDPHoleServiceProxy() + private let superServiceProxy: SDLSuperServiceProxy + private let udpHoleServiceProxy: SDLUDPHoleServiceProxy + private let packetOutboundActor: PacketOutboundActor private let publicDnsServers = ["223.5.5.5", "119.29.29.29"] @@ -82,14 +78,14 @@ actor SDLContextActor { // 本地ipv6地址信息探测 private var ipv6AssistClient: SDLIPV6AssistClient? - private let sessionManager = SessionManager() + private let sessionManager: SessionManager nonisolated private let arpServer: ArpServer // 内部socket通讯 // 改为基于 App Group + Darwin Notification 的通知 // 流量统计 - nonisolated private let flowTracer = SDLFlowTracer() + nonisolated private let flowTracer: SDLFlowTracer nonisolated private let provider: NEPacketTunnelProvider @@ -101,17 +97,42 @@ actor SDLContextActor { private var stunRequestTask: Task? public init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher) { + let puncherActor = SDLPuncherActor() + let proberActor = SDLNATProberActor(addressArray: config.stunProbeSocketAddressArray) + let sessionManager = SessionManager() + let arpServer = ArpServer() + let flowTracer = SDLFlowTracer() + let policyService = PolicyService(identityId: config.identityId) + let superServiceProxy = SDLSuperServiceProxy() + let udpHoleServiceProxy = SDLUDPHoleServiceProxy() + self.provider = provider self.config = config self.rsaCipher = rsaCipher - self.puncherActor = SDLPuncherActor() - self.proberActor = SDLNATProberActor(addressArray: config.stunProbeSocketAddressArray) + self.puncherActor = puncherActor + self.proberActor = proberActor - self.arpServer = ArpServer() + self.sessionManager = sessionManager + self.arpServer = arpServer + self.flowTracer = flowTracer // 权限控制 - self.policyService = PolicyService(identityId: config.identityId) + self.policyService = policyService + self.superServiceProxy = superServiceProxy + self.udpHoleServiceProxy = udpHoleServiceProxy + self.packetOutboundActor = PacketOutboundActor( + provider: provider, + config: config, + dataCipher: nil, + sessionManager: sessionManager, + arpServer: arpServer, + puncherActor: puncherActor, + policyService: policyService, + superServiceProxy: superServiceProxy, + udpHoleServiceProxy: udpHoleServiceProxy, + flowTracer: flowTracer + ) } public func start() async { @@ -125,6 +146,7 @@ actor SDLContextActor { await self?.handleDNSEvent(event) } self.dnsService = dnsService + await self.packetOutboundActor.updateDNSService(dnsService) await dnsService.start() let udpHoleService = SDLUDPHoleService(proberActor: self.proberActor) { [weak self] event in @@ -161,6 +183,7 @@ actor SDLContextActor { let dnsService = self.dnsService self.dnsService = nil + await self.packetOutboundActor.updateDNSService(nil) await dnsService?.stop() await self.superServiceProxy.stop() @@ -168,6 +191,7 @@ actor SDLContextActor { self.sessionToken = nil self.dataCipher = nil self.natType = .blocked + await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: nil) await self.ipv6AssistClient?.stop() self.ipv6AssistClient = nil @@ -293,6 +317,7 @@ extension SDLContextActor { return } + await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: self.dataCipher) SDLLogger.log("[SDLContext] registerSuperAck, use algorithm \(algorithm), key len: \(key.count)") // 服务器分配的tun网卡信息 do { @@ -490,7 +515,7 @@ extension SDLContextActor { switch plan.action { case .sendARPReply(let dstMac, let responseData): SDLLogger.log("[SDLContext] get arp request packet") - await self.routeLayerPacket(dstMac: dstMac, type: .arp, data: responseData) + await self.packetOutboundActor.routeLayerPacket(dstMac: dstMac, type: .arp, data: responseData) case .appendARP(let ip, let mac): SDLLogger.log("[SDLContext] get arp response packet") await self.arpServer.append(ip: ip, mac: mac) @@ -589,7 +614,7 @@ extension SDLContextActor { private func handlePacketReaderEvent(_ event: SDLPacketReaderService.Event) async { switch event { case .packet(let packet): - await self.dealTunPacket(packet: packet) + await self.packetOutboundActor.handleTunPacket(packet) } } @@ -600,7 +625,8 @@ extension SDLContextActor { } else { self.config.exitNode = nil } - try await self.setNetworkSettings(config: config, dnsServer: DNSHelper.dnsServer) + await self.packetOutboundActor.updateRuntime(config: self.config, dataCipher: self.dataCipher) + try await self.setNetworkSettings(config: self.config, dnsServer: DNSHelper.dnsServer) } // MARK: 网络改变时需要重新配置网络信息 @@ -667,100 +693,4 @@ extension SDLContextActor { return ipv4DnsServers.map { NEIPv4Route(destinationAddress: $0, subnetMask: "255.255.255.255") } } - // 处理读取的每个数据包, Tun收到的包的一级路由 - private func dealTunPacket(packet: IPPacket) async { - let router = SDLTunPacketRouter(networkAddress: self.config.networkAddress, exitNode: self.config.exitNode) - - let decision = router.route(packet: packet) - - // 外部出去的数据,需要建立FlowSession - // 外部数据进来的时候需要查找 - if decision.shouldTrackFlow, let flowSession = packet.flowSession() { - await self.policyService.flowSessionManager.updateSession(flowSession) - //SDLLogger.shared.log("[SDLContext] flow_session: \(flowSession)", level: .debug) - } - - await self.handleTunRouteDecision(decision) - } - - 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("[SDLContext] get cloud dns request: \(name)") - await self.dnsService?.forward(ipPacketData: ipPacketData) - case .localDNS(let name, let payload, let tracker): - SDLLogger.log("[SDLContext] 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("[SDLContext] 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("[SDLContext] dstIp: \(asIpAddress(ip)) same network", for: .trace) - case .exitNode, .dnsExitNode: - SDLLogger.log("[SDLContext] use exit_node: \(asIpAddress(ip))", for: .trace) - } - - // 查找arp缓存中是否有目标mac地址 - if let dstMac = await self.arpServer.query(ip: ip) { - SDLLogger.log("[SDLContext] dstIp: \(asIpAddress(ip)), dst_mac is: \(SDLUtil.formatMacAddress(mac: dstMac))", for: .trace) - await self.routeLayerPacket(dstMac: dstMac, type: type, data: data) - } - else { - SDLLogger.log("[SDLContext] dstIp: \(asIpAddress(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.config.networkAddress, - identityID: self.config.identityId, - dataCipher: self.dataCipher, - sessionManager: self.sessionManager - ) - } - - private func routeLayerPacket(dstMac: Data, type: LayerPacket.PacketType, data: Data) async { - // 将数据封装层2层的数据包 - // 构造数据包 - 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): - // 通过super_node进行转发 - await self.sendSuperPacket(type: .data, data: payload) - case .peer(let payload, let session): - // 通过session发送到对端 - SDLLogger.log("[SDLContext] 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): - // 通过super_node进行转发 - await self.sendSuperPacket(type: .data, data: payload) - SDLLogger.log("[SDLContext] step 5 send packet by super: \(self.config.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) - } - } - } - }