MacOS下Per-App VPN从NEPacketTunnelFlow获取PID遇sourceAppAuditToken为空
问题分析
NEPacketTunnelProvider的设计定位是处理隧道层面的数据包转发,而非应用层流量过滤,因此系统不会自动将sourceAppAuditToken附加到NEPacketTunnelFlow的数据包元数据中——该字段仅为NEFilterProvider(内容过滤扩展)设计,两者的权限模型和数据传递逻辑完全不同,这就是你始终拿到nil的核心原因。
替代解决方案:通过IP+端口映射获取PID
在MacOS上,可通过解析系统网络连接表,将数据包的源IP、端口与进程ID做关联,具体实现如下:
1. 提取数据包的源IP和端口
从NEPacket的networkProtocol字段中获取关键信息:
self.packetFlow.readPacketObjects { packets in for packet in packets { guard let networkProtocol = packet.networkProtocol else { continue } let sourceAddr = networkProtocol.sourceAddress let sourcePort = networkProtocol.sourcePort // 调用方法通过IP+端口查找PID if let pid = self.pidForSocketAddress(address: sourceAddr, port: sourcePort) { log.debug("PID: \(pid)") } else { log.debug("无法匹配到对应进程") } } }
2. 解析系统网络连接表(sysctl实现)
通过sysctl读取系统TCP/UDP连接表,匹配目标IP和端口对应的PID:
import Foundation import System // 适配MacOS的TCP连接结构体(需根据系统版本调整字段) struct inpcb { let inp_len: UInt16 let inp_family: UInt8 let inp_type: UInt8 let inp_protocol: UInt8 let inp_flags: UInt16 let inp_lport: UInt16 let inp_laddr: in_addr let inp_faddr: in_addr let inp_pid: pid_t // IPv6相关字段需额外添加,此处仅示例IPv4逻辑 } func pidForSocketAddress(address: sockaddr_storage, port: UInt16) -> pid_t? { var mib: [Int32] = [CTL_NET, PF_INET, SOCK_STREAM, NET_INET_DIAG, NET_INET_DIAG_PCB] var len: size_t = 0 // 获取缓冲区大小 guard sysctl(&mib, UInt32(mib.count), nil, &len, nil, 0) >= 0 else { return nil } var buffer = [UInt8](repeating: 0, count: len) guard sysctl(&mib, UInt32(mib.count), &buffer, &len, nil, 0) >= 0 else { return nil } // 遍历所有连接条目 var offset = 0 while offset < len { let pcb = buffer.withUnsafeBytes { ptr in ptr.baseAddress!.advanced(by: offset).assumingMemoryBound(to: inpcb.self) }.pointee // 匹配端口和IP地址 if pcb.inp_lport == port, address.ss_family == AF_INET { let srcAddr = address.withUnsafeBytes { ptr in ptr.baseAddress!.assumingMemoryBound(to: sockaddr_in.self).pointee.sin_addr.s_addr } if pcb.inp_laddr.s_addr == srcAddr { return pcb.inp_pid } } offset += Int(pcb.inp_len) } return nil }
3. 沙箱权限配置
若你的Packet Tunnel Extension启用了沙箱,需在entitlements文件中添加以下权限:
<key>com.apple.security.network.client</key> <true/> <key>com.apple.security.network.server</key> <true/> <key>com.apple.security.sysctl</key> <array> <string>net.inet.tcp.pcblist</string> <string>net.inet.udp.pcblist</string> </array>
额外注意事项
- 该方法仅支持TCP/UDP协议,ICMP等无端口协议无法匹配PID;
- 若多个进程复用同一IP:端口(如启用
SO_REUSEPORT),需额外逻辑处理多PID场景; - MacOS 12+对
sysctl访问限制更严格,若遇到权限问题,可改用libproc库的proc_listallpids+proc_pidinfo遍历进程并匹配网络连接。
内容的提问来源于stack exchange,提问作者emanuele-em
相关产品推荐
相关产品推荐

