// // NetworkInterface.swift // punchnet // // Created by 安礼成 on 2025/8/3. // // // NetworkInterface.swift // Tun // // Created by 安礼成 on 2024/1/19. // import Foundation import Network public struct NetworkInterface { public let name: String public let ip: String public let netmask: String } public struct NetworkInterfaceManager { /** 获取网卡信息, (let name: String let ip: String let netmask: String) */ public static func getInterfaces() -> [NetworkInterface] { var interfaces: [NetworkInterface] = [] // Get list of all interfaces on the local machine: var ifaddr : UnsafeMutablePointer? = nil if getifaddrs(&ifaddr) == 0 { // For each interface ... var ptr = ifaddr while( ptr != nil) { let flags = Int32(ptr!.pointee.ifa_flags) var addr = ptr!.pointee.ifa_addr.pointee // Check for running IPv4, IPv6 interfaces. Skip the loopback interface. if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) { if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) { var mask = ptr!.pointee.ifa_netmask.pointee // Convert interface address to a human readable string: let zero = CChar(0) var hostname = [CChar](repeating: zero, count: Int(NI_MAXHOST)) var netmask = [CChar](repeating: zero, count: Int(NI_MAXHOST)) if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0) { let address = String(cString: hostname) let name = ptr!.pointee.ifa_name! let ifname = String(cString: name) if (getnameinfo(&mask, socklen_t(mask.sa_len), &netmask, socklen_t(netmask.count), nil, socklen_t(0), NI_NUMERICHOST) == 0) { let netmaskIP = String(cString: netmask) interfaces.append(NetworkInterface(name: ifname, ip: address, netmask: netmaskIP)) } } } } ptr = ptr!.pointee.ifa_next } freeifaddrs(ifaddr) } 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 } }