fix holeControlPlane

This commit is contained in:
anlicheng 2026-05-28 01:26:45 +08:00
parent eca022cae0
commit 1907c4347b
2 changed files with 140 additions and 100 deletions

View File

@ -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<SDLV6Info>.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 {
private func prepareTunnelNotifier() {
// noticeClient
@ -432,15 +434,8 @@ 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)
}
private func sendPacket(type: SDLPacketType, data: Data, remoteAddress: SocketAddress) async {
switch remoteAddress {
@ -452,10 +447,8 @@ extension SDLContextActor {
SDLLogger.log("[SDLContext] unsupported socket family: \(remoteAddress)", for: .debug)
}
}
}
// MARK: Super
extension SDLContextActor {
private func handleSuperDecision(_ decision: SuperDecision) async {
switch decision {
@ -530,10 +523,8 @@ extension SDLContextActor {
self.config.acl = acl
}
}
// MARK: DNS service events
extension SDLContextActor {
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 {
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)
}
}
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-nodenatudpnat
if let addressType = Self.addressType(from: remoteAddress),
let session = Session(dstMac: register.srcMac, natAddress: remoteAddress, addressType: addressType) {
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)
} 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)")
}
}
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 {
// ip: 0.0.0.0
public func updateExitNode(exitNodeIp: String) async throws {

View File

@ -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
}
}
}