diff --git a/Tun/NetworkInterface.swift b/Tun/NetworkInterface.swift index 8455287..7946eaf 100644 --- a/Tun/NetworkInterface.swift +++ b/Tun/NetworkInterface.swift @@ -14,6 +14,7 @@ // import Foundation +import Network public struct NetworkInterface { public let name: String @@ -71,5 +72,69 @@ public struct NetworkInterfaceManager { return interfaces } + + // 获取本地网卡中的公网IPv6地址 + public static func getPublicIPv6Address() -> String? { + return self.getPublicIPv6Interface()?.ip + } + // 获取本地网卡中的公网IPv6网卡 + public static func getPublicIPv6Interface() -> NetworkInterface? { + let interfaces = self.getInterfaces() + + return interfaces.first { interface in + !interface.name.hasPrefix("utun") + && self.isPublicIPv6(interface.ip) + } + } + + /// 判断一个 IPv6 字符串是否是公网 IPv6 + public static func isPublicIPv6(_ ipString: String) -> Bool { + let normalizedIp = String(ipString.split(separator: "%", maxSplits: 1, omittingEmptySubsequences: false).first ?? "") + guard let ipv6 = IPv6Address(normalizedIp) else { + return false + } + return self.isPublicIPv6(ipv6.rawValue) + } + + /// 判断 16 字节 IPv6 地址是否是公网 IPv6 + public static func isPublicIPv6(_ raw: Data) -> Bool { + guard raw.count == 16 else { + return false + } + + let bytes = [UInt8](raw) + + // 1. 排除 unspecified :: + if bytes.allSatisfy({ $0 == 0 }) { + return false + } + + // 2. 排除 loopback ::1 + if bytes.dropLast().allSatisfy({ $0 == 0 }) && bytes[15] == 1 { + return false + } + + // 3. 排除 multicast ff00::/8 + if bytes[0] == 0xFF { + return false + } + + // 4. 排除 link-local fe80::/10 + // 即首字节 0xFE,第二字节前两位是 10(二进制) + if bytes[0] == 0xFE && (bytes[1] & 0xC0) == 0x80 { + return false + } + + // 5. 排除 ULA fc00::/7 + // 即首字节前 7 位是 1111110 + if (bytes[0] & 0xFE) == 0xFC { + return false + } + + // 6. 判断是否属于 2000::/3 + // 即首字节前 3 位是 001 + return (bytes[0] & 0xE0) == 0x20 + } + } diff --git a/Tun/Punchnet/Actors/SDLContextActor.swift b/Tun/Punchnet/Actors/SDLContextActor.swift index b319eda..998e680 100644 --- a/Tun/Punchnet/Actors/SDLContextActor.swift +++ b/Tun/Punchnet/Actors/SDLContextActor.swift @@ -550,20 +550,9 @@ actor SDLContextActor { } private func makeCurrentV6Info() -> SDLV6Info? { - guard let port = self.udpHoleV6LocalAddress?.port else { - return nil - } - - let interfaces = NetworkInterfaceManager.getInterfaces() - let interface = interfaces.first { interface in - interface.ip.contains(":") - && !interface.name.hasPrefix("utun") - && !interface.ip.hasPrefix("fe80:") - && interface.ip != "::" - } - - guard let interface, - let ipData = SDLUtil.ipv6StrToData(interface.ip) else { + guard let port = self.udpHoleV6LocalAddress?.port, + let ip = NetworkInterfaceManager.getPublicIPv6Address(), + let ipData = SDLUtil.ipv6StrToData(ip) else { return nil }