// // SDLTunPacketRouter.swift // Tun // // Created by 安礼成 on 2026/4/14. // import Foundation struct SDLTunPacketRouter { enum DropReason: String { case invalidDNSRequest case noRoute } enum ForwardKind { case sameNetwork case exitNode case dnsExitNode } enum RouteDecision { case loopback(ipPacketData: Data) case cloudDNS(name: String, ipPacketData: Data) case localDNS(name: String, payload: Data, tracker: DNSLocalClient.DNSTracker) case forwardToNextHop(ip: UInt32, type: LayerPacket.PacketType, data: Data, kind: ForwardKind) case drop(reason: DropReason) } let networkAddress: SDLConfiguration.NetworkAddress let exitNode: SDLConfiguration.ExitNode? func route(packet: IPPacket, now: Date = Date()) -> RouteDecision { let dstIp = packet.header.destination // 本地通讯, 目标地址是本地服务器的ip地址 if dstIp == self.networkAddress.ip { return .loopback(ipPacketData: packet.data) } // 处理dns的解析 if let dnsDecision = self.routeDNS(packet: packet, now: now) { return dnsDecision } // 判断目标地址是否和当前的网络地址是否在同一个网段 // 只有在同一个网段的ip数据才直接发送 if SDLUtil.inSameNetwork(ip: dstIp, compareIp: self.networkAddress.ip, maskLen: self.networkAddress.maskLen) { return .forwardToNextHop(ip: dstIp, type: .ipv4, data: packet.data, kind: .sameNetwork) } // 不在同一个网段的数据,看是否配置了网络出口, 如果配置了,转发数据个网络出口,否则丢弃 if let exitNode = self.exitNode { return .forwardToNextHop(ip: exitNode.exitNodeIp, type: .ipv4, data: packet.data, kind: .exitNode) } return .drop(reason: .noRoute) } private func routeDNS(packet: IPPacket, now: Date) -> RouteDecision? { guard DNSHelper.isDnsRequestPacket(ipPacket: packet) else { return nil } guard case .udp(let udpPacket) = packet.transportPacket else { return .drop(reason: .invalidDNSRequest) } // 数据是通过offset解析的, dns查询必然是udp包 let payloadOffset = udpPacket.payloadOffset let dnsParser = DNSParser(data: packet.data, offset: payloadOffset) guard let dnsMessage = dnsParser.parse(), let name = dnsMessage.questions.first?.name else { return .drop(reason: .invalidDNSRequest) } // 如果是内部域名,则转发整个ip包的内容到云端服务器 if name.contains(self.networkAddress.networkDomain) { return .cloudDNS(name: name, ipPacketData: packet.data) } // 如果开启了出口节点,则转发给出口节点 if let exitNode = self.exitNode { return .forwardToNextHop(ip: exitNode.exitNodeIp, type: .ipv4, data: packet.data, kind: .dnsExitNode) } // 通过本地的dns解析,发送的是udp的payload部分 let dnsPayload = Data(packet.data[payloadOffset..