diff --git a/Tun/Context/SDLContextActor.swift b/Tun/Context/SDLContextActor.swift index 1a66866..4e01ca7 100644 --- a/Tun/Context/SDLContextActor.swift +++ b/Tun/Context/SDLContextActor.swift @@ -30,6 +30,7 @@ actor SDLContextActor { private let dnsLocalService: DNSLocalService private let superService: SDLSuperService private let superControlPlane: SDLSuperControlPlane + private let holeControlPlane: SDLHoleControlPlane private let udpHoleService: SDLUDPHoleService private let udpHoleV6Service: SDLUDPHoleV6Service private let packetOutboundActor: PacketOutboundActor @@ -76,6 +77,7 @@ actor SDLContextActor { let dnsCloudService = DNSCloudService(serverIP: config.serverEndpoint.ip) let dnsLocalService = DNSLocalService(publicDnsServers: Self.publicDnsServers) let superControlPlane = SDLSuperControlPlane(config: config, rsaCipher: rsaCipher) + let holeControlPlane = SDLHoleControlPlane(networkAddress: config.networkAddress) let tunNetworkManager = SDLTunNetworkManager(provider: provider) let ipv6AssistPair = AsyncStream.makeStream(of: Optional.self, bufferingPolicy: .bufferingNewest(1)) let packetOutboundActor = PacketOutboundActor( @@ -122,6 +124,7 @@ actor SDLContextActor { self.dnsLocalService = dnsLocalService self.superService = superService self.superControlPlane = superControlPlane + self.holeControlPlane = holeControlPlane self.udpHoleService = udpHoleService self.udpHoleV6Service = udpHoleV6Service self.packetOutboundActor = packetOutboundActor @@ -410,16 +413,15 @@ actor SDLContextActor { } -// MARK: probe网络类型 extension SDLContextActor { + // MARK: probe网络类型 + private func setNatType(natType: SDLNATProberActor.NatType) { self.natType = natType } -} -// MARK: Notifier通知机制 -extension SDLContextActor { + // MARK: Notifier通知机制 private func prepareTunnelNotifier() { // 启动noticeClient @@ -432,16 +434,9 @@ extension SDLContextActor { private func publishTunnelEvent(code: Int? = nil, message: String) { SDLTunnelAppNotifier.shared.publish(code: code, message: message) } -} -// MARK: 数据发送 -extension SDLContextActor { - - // 发送给peer的数据 - private func sendPeerPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async { - await self.sendPacket(type: type, data: data, remoteAddress: remoteAddress) - } - + // MARK: 数据发送 + private func sendPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async { switch remoteAddress { case .v4: @@ -452,10 +447,8 @@ extension SDLContextActor { SDLLogger.log("[SDLContext] unsupported socket family: \(remoteAddress)", for: .debug) } } -} -// MARK: 处理和Super之间的通讯 -extension SDLContextActor { + // MARK: 处理和Super之间的通讯 private func handleSuperDecision(_ decision: SuperDecision) async { switch decision { @@ -530,10 +523,8 @@ extension SDLContextActor { self.config.acl = acl } -} - -// MARK: DNS service events -extension SDLContextActor { + // MARK: DNS service events + private func handleDNSEvent(_ event: DNSEvent) async { switch event { case .packet(let packet): @@ -541,91 +532,29 @@ extension SDLContextActor { self.provider.packetFlow.writePacketObjects([nePacket]) } } -} -// MARK: 处理从Hole收到的数据 -extension SDLContextActor { + // MARK: 处理从Hole收到的数据 private func handleUDPHoleControlEvent(_ event: SDLUDPHoleService.Event) async { - switch event { - case .ready(let localAddress): - SDLLogger.log("[SDLContext] udpHole ready: \(localAddress)") - case .natType(let natType): + let decisions = self.holeControlPlane.handle(event) + for decision in decisions { + await self.handleHoleDecision(decision) + } + } + + private func handleHoleDecision(_ decision: HoleDecision) async { + switch decision { + case .updateNatType(let natType): self.setNatType(natType: natType) - SDLLogger.log("[SDLContext] nat_type is: \(natType)") - case .packet(let remoteAddress, let message): - await self.handleUDPHolePacket(remoteAddress: remoteAddress, message: message) - case .closed(let error): - SDLLogger.log("[SDLContext] udpHole closed: \(error)", for: .debug) - } - } - - private func handleUDPHolePacket(remoteAddress: SocketAddress, message: SDLHoleControlMessage) async { - switch message { - case .stunReply(_), .stunProbeReply(_): - SDLLogger.log("[SDLContext] get a stun reply", for: .debug) - case .register(let register): - try? await self.handleRegister(remoteAddress: remoteAddress, register: register) - case .registerAck(let registerAck): - await self.handleRegisterAck(remoteAddress: remoteAddress, registerAck: registerAck) + case .sendPacket(let type, let data, let remoteAddress): + await self.sendPacket(type: type, data: data, remoteAddress: remoteAddress) + case .addSession(let session): + await self.sessionManager.addSession(session: session) } } - private func handleRegister(remoteAddress: SocketAddress, register: SDLRegister) async throws { - let networkAddr = config.networkAddress - SDLLogger.log("[SDLContext] register packet: \(register), network_address: \(networkAddr)") - - // 判断目标地址是否是tun的网卡地址, 并且是在同一个网络下 - if register.dstMac == networkAddr.mac && register.networkID == networkAddr.networkId { - // 回复ack包 - var registerAck = SDLRegisterAck() - registerAck.networkID = networkAddr.networkId - registerAck.srcMac = networkAddr.mac - registerAck.dstMac = register.srcMac - - await self.sendPeerPacket(type: .registerAck, data: try registerAck.serializedData(), remoteAddress: remoteAddress) - // 这里需要建立到来源的会话, 在复杂网络下,通过super-node查询到的nat地址不一定靠谱,需要通过udp包的来源地址作为nat地址 - if let addressType = Self.addressType(from: remoteAddress), - let session = Session(dstMac: register.srcMac, natAddress: remoteAddress, addressType: addressType) { - await self.sessionManager.addSession(session: session) - } else { - SDLLogger.log("[SDLContext] didReadRegister get unsupported remoteAddress: \(remoteAddress)", for: .debug) - } - } else { - SDLLogger.log("[SDLContext] didReadRegister get a invalid packet, because dst_ip not matched: \(register.dstMac)") - } - } + // MARK: 和Stun相关的心跳机制 - private func handleRegisterAck(remoteAddress: SocketAddress, registerAck: SDLRegisterAck) async { - // 判断目标地址是否是tun的网卡地址, 并且是在同一个网络下 - let networkAddr = config.networkAddress - if registerAck.dstMac == networkAddr.mac && registerAck.networkID == networkAddr.networkId { - if let addressType = Self.addressType(from: remoteAddress), - let session = Session(dstMac: registerAck.srcMac, natAddress: remoteAddress, addressType: addressType) { - await self.sessionManager.addSession(session: session) - } else { - SDLLogger.log("[SDLContext] didReadRegisterAck get unsupported remoteAddress: \(remoteAddress)", for: .debug) - } - } else { - SDLLogger.log("[SDLContext] didReadRegisterAck get a invalid packet, because dst_mac not matched: \(registerAck.dstMac)") - } - } - - private static func addressType(from remoteAddress: SocketAddress) -> Session.AddressType? { - switch remoteAddress { - case .v4: - return .v4 - case .v6: - return .v6 - default: - return nil - } - } - -} - -// MARK: 和Stun相关的心跳机制 -extension SDLContextActor { private func runStunRequestOnce() async throws { let probeReply = try? await self.ipv6AssistClient?.probe(requestTimeout: .seconds(3)) @@ -637,10 +566,8 @@ extension SDLContextActor { await self.superControlPlane.sendStunRequest(natType: self.natType, v6Info: probeReply?.v6Info) } -} -// MARK: NEPacketTunnelProvider相关的逻辑 -extension SDLContextActor { + // MARK: NEPacketTunnelProvider相关的逻辑 // 取消出口节点的时候,ip地址为: 0.0.0.0 public func updateExitNode(exitNodeIp: String) async throws { diff --git a/Tun/Context/SDLHoleControlPlane.swift b/Tun/Context/SDLHoleControlPlane.swift new file mode 100644 index 0000000..b5e8b7e --- /dev/null +++ b/Tun/Context/SDLHoleControlPlane.swift @@ -0,0 +1,113 @@ +// +// SDLHoleControlPlane.swift +// Tun +// +// Created by Codex on 2026/5/28. +// + +import Foundation +import NIOCore + +enum HoleDecision { + case updateNatType(SDLNATProberActor.NatType) + case sendPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) + case addSession(Session) +} + +struct SDLHoleControlPlane { + private let networkAddress: SDLConfiguration.NetworkAddress + + init(networkAddress: SDLConfiguration.NetworkAddress) { + self.networkAddress = networkAddress + } + + func handle(_ event: SDLUDPHoleService.Event) -> [HoleDecision] { + switch event { + case .ready(let localAddress): + SDLLogger.log("[SDLContext] udpHole ready: \(localAddress)") + return [] + case .natType(let natType): + SDLLogger.log("[SDLContext] nat_type is: \(natType)") + return [ + .updateNatType(natType) + ] + case .packet(let remoteAddress, let message): + return self.handlePacket(remoteAddress: remoteAddress, message: message) + case .closed(let error): + SDLLogger.log("[SDLContext] udpHole closed: \(error)", for: .debug) + return [] + } + } + + private func handlePacket(remoteAddress: SocketAddress, message: SDLHoleControlMessage) -> [HoleDecision] { + switch message { + case .stunReply, .stunProbeReply: + SDLLogger.log("[SDLContext] get a stun reply", for: .debug) + return [] + case .register(let register): + return self.handleRegister(remoteAddress: remoteAddress, register: register) + case .registerAck(let registerAck): + return self.handleRegisterAck(remoteAddress: remoteAddress, registerAck: registerAck) + } + } + + private func handleRegister(remoteAddress: SocketAddress, register: SDLRegister) -> [HoleDecision] { + SDLLogger.log("[SDLContext] register packet: \(register), network_address: \(self.networkAddress)") + var decisions: [HoleDecision] = [] + + guard register.dstMac == self.networkAddress.mac && register.networkID == self.networkAddress.networkId else { + SDLLogger.log("[SDLContext] didReadRegister get a invalid packet, because dst_ip not matched: \(register.dstMac)") + return decisions + } + + var registerAck = SDLRegisterAck() + registerAck.networkID = self.networkAddress.networkId + registerAck.srcMac = self.networkAddress.mac + registerAck.dstMac = register.srcMac + + if let data = try? registerAck.serializedData() { + decisions.append(.sendPacket(type: .registerAck, data: data, remoteAddress: remoteAddress)) + } + + if let session = self.makeSession(dstMac: register.srcMac, remoteAddress: remoteAddress) { + decisions.append(.addSession(session)) + } else { + SDLLogger.log("[SDLContext] didReadRegister get unsupported remoteAddress: \(remoteAddress)", for: .debug) + } + + return decisions + } + + private func handleRegisterAck(remoteAddress: SocketAddress, registerAck: SDLRegisterAck) -> [HoleDecision] { + guard registerAck.dstMac == self.networkAddress.mac && registerAck.networkID == self.networkAddress.networkId else { + SDLLogger.log("[SDLContext] didReadRegisterAck get a invalid packet, because dst_mac not matched: \(registerAck.dstMac)", for: .debug) + return [] + } + + guard let session = self.makeSession(dstMac: registerAck.srcMac, remoteAddress: remoteAddress) else { + SDLLogger.log("[SDLContext] didReadRegisterAck get unsupported remoteAddress: \(remoteAddress)", for: .debug) + return [] + } + + return [.addSession(session)] + } + + private func makeSession(dstMac: Data, remoteAddress: SocketAddress) -> Session? { + guard let addressType = Self.addressType(from: remoteAddress) else { + return nil + } + + return Session(dstMac: dstMac, natAddress: remoteAddress, addressType: addressType) + } + + private static func addressType(from remoteAddress: SocketAddress) -> Session.AddressType? { + switch remoteAddress { + case .v4: + return .v4 + case .v6: + return .v6 + default: + return nil + } + } +}