115 lines
3.9 KiB
Swift
115 lines
3.9 KiB
Swift
//
|
||
// 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..<packet.data.count])
|
||
let tracker = DNSLocalClient.DNSTracker(
|
||
transactionID: dnsMessage.transactionID,
|
||
clientIP: packet.header.source,
|
||
clientPort: udpPacket.srcPort,
|
||
createdAt: now
|
||
)
|
||
return .localDNS(name: name, payload: dnsPayload, tracker: tracker)
|
||
}
|
||
}
|
||
|
||
extension SDLTunPacketRouter.RouteDecision {
|
||
|
||
var shouldTrackFlow: Bool {
|
||
switch self {
|
||
case .forwardToNextHop(_, _, _, let kind):
|
||
switch kind {
|
||
case .sameNetwork, .exitNode:
|
||
return true
|
||
case .dnsExitNode:
|
||
return false
|
||
}
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
}
|