141 lines
4.6 KiB
Swift
141 lines
4.6 KiB
Swift
//
|
||
// 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<ifaddrs>? = 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
|
||
}
|
||
|
||
}
|