ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

windows 驱动实例分析系列: wireguard-nt驱动分析-driver篇(三)

windows 驱动实例分析系列: wireguard-nt驱动分析-driver篇(三) WireGuardNT 驱动代码解析 - 第三篇辅助模块与同步机制1. 概述本文为驱动代码解析的第三篇聚焦于支撑 WireGuardNT 核心功能的辅助模块包括 AllowedIPs 高效查找、Cookie 防 DoS、速率限制、RCU 同步、内存管理以及对等体管理。这些模块共同保证了驱动的性能、安全性和可维护性。涉及的主要源文件allowedips.c/allowedips.h– AllowedIPs 查找树的实现基于 Radix Tree。cookie.c/cookie.h– Cookie 生成与验证用于 DoS 防御。ratelimiter.c/ratelimiter.h– 基于令牌桶的速率限制器用于握手请求。rcu.c/rcu.h– Read-Copy-Update 同步机制用于无锁读取。memory.c/memory.h– 内存池管理NET_BUFFER 缓存、数据包分配。peer.c/peer.h– 对等体对象的创建、引用计数和生命周期管理。peerlookup.c/peerlookup.h– 公钥哈希表和索引哈希表用于快速查找对等体。2. AllowedIPs 查找树allowedips.c / allowedips.hAllowedIPs 用于将 IP 地址前缀映射到对应的对等体实现路由选择。WireGuard 使用一种紧凑的二进制 TrieRadix Tree结构支持 IPv432 位和 IPv6128 位前缀。2.1 数据结构ALLOWEDIPS_NODE树节点包含指向左右子节点的指针Bit[2]、CIDR、位位置信息、存储的 IP 前缀Bits[16]、指向对等体的 RCU 指针Peer以及用于链表和 RCU 回调的联合体。ALLOWEDIPS_TABLE包含两个根节点指针Root4和Root6分别用于 IPv4 和 IPv6。2.2 核心操作插入Add根据 IP 和 CIDR 插入或更新节点。若节点已存在则更新其对等体否则创建新节点并根据公共前缀长度分裂现有节点。使用LOOKASIDE_LIST_EX缓存节点减少内存分配开销。删除Remove根据 IP 和 CIDR 查找并移除节点若对等体匹配则删除。若删除后节点无子节点且父节点无对等体则收缩树。查找Lookup从根节点开始根据 IP 逐位向下遍历找到最长匹配前缀的节点返回对应的对等体强引用。查找过程在 RCU 保护下进行支持并发读取。2.3 优化细节使用SwapEndian将网络字节序转换为主机字节序便于使用FindLastSet32/64/128快速计算公共前缀长度。节点中预计算BitAtA和BitAtB加速查找时的位选择。插入时使用NodePlacement辅助函数定位插入位置避免重复遍历。删除时通过RemoveNode处理节点收缩并利用 RCU 延迟释放。2.4 对外接口AllowedIpsInsertV4/V6、AllowedIpsRemoveV4/V6供外部调用由 IOCTL 触发。AllowedIpsRemoveByPeer移除对等体关联的所有前缀在删除对等体时调用。AllowedIpsLookupDst/Src根据 IP 头目的地址或源地址查找对等体用于数据包路由和反向路径验证。AllowedIpsReadNode遍历节点用于获取配置。2.5 自测试selftest/allowedips.c在调试版本中执行大量插入、删除、查找和遍历测试验证树的正确性。3. Cookie 机制cookie.c / cookie.hCookie 用于防御 DoS 攻击如伪造源地址的握手洪水。其原理是在握手消息中强制客户端计算一个基于密钥和源地址的 MACMac1/Mac2服务端验证后才会进行昂贵的握手计算。3.1 核心数据结构COOKIE_CHECKER每个设备一个包含用于生成 Cookie 的密钥Secret、加密密钥CookieEncryptionKey、Mac1 密钥MessageMac1Key以及锁和最近生成时间。COOKIE每个对等体一个缓存已收到的有效 Cookie 和用于验证的密钥。3.2 Cookie 生成与验证流程Mac1 计算对握手消息不含 Mac 字段使用设备的MessageMac1Key计算 Blake2s 哈希作为 Mac1。Cookie 验证当系统负载较高HandshakeRxQueueLen过大时要求客户端提供 Mac2。Mac2 是使用服务端生成的 Cookie基于源地址和设备密钥对消息再次计算的哈希。Cookie 消息若客户端未提供 Mac2服务端返回MESSAGE_HANDSHAKE_COOKIE其中包含加密后的 Cookie使用 XChaCha20-Poly1305 加密。客户端解密并保存 Cookie下次握手时使用该 Cookie 计算 Mac2。3.3 关键函数CookieCheckerPrecomputeDeviceKeys在设置私钥时预计算设备密钥。CookieCheckerPrecomputePeerKeys在对等体创建时预计算对等体的解密密钥。CookieValidatePacket验证握手包的 Mac1和 Mac2返回状态VALID_MAC_WITH_COOKIE、VALID_MAC_BUT_NO_COOKIE、INVALID_MAC等。CookieAddMacToPacket发送握手消息时添加 Mac1和 Mac2如果有 Cookie。CookieMessageCreate生成 Cookie 响应消息。CookieMessageConsume处理收到的 Cookie 响应解密并保存 Cookie。3.4 安全考虑使用 XChaCha20-Poly1305 加密 Cookie确保保密性。密钥定期更新基于时间。使用 Blake2s 哈希抵抗碰撞。4. 速率限制器ratelimiter.c / ratelimiter.h速率限制器用于限制来自同一 IP 的握手请求频率防止暴力攻击。4.1 数据结构RATELIMITER_ENTRY每个源 IPIPv4 或 IPv6 /64一个包含上次时间戳、当前令牌数、IP 地址以及哈希表节点。全局哈希表TableV4和TableV6用于存储条目使用Hsiphash计算哈希。使用LOOKASIDE_LIST_EX缓存条目对象。4.2 令牌桶算法每个条目维护Tokens最大为TOKEN_MAX和LastTime。每次请求时计算时间差补充令牌不超过最大然后扣除一个PACKET_COST。若Tokens PACKET_COST则允许否则拒绝。PACKETS_PER_SECOND 20PACKETS_BURSTABLE 5允许短期突发。4.3 GC垃圾回收线程定期扫描哈希表删除超过 1 秒未活动的条目防止表过大。使用 RCU 保护并发访问删除时调用HlistDelRcu。4.4 对外接口RatelimiterAllow检查是否允许来自给定源地址的请求。RatelimiterDriverEntry/RatelimiterUnload初始化/卸载。5. RCU 同步机制rcu.c / rcu.hRCURead-Copy-Update用于保护读多写少的数据结构如 AllowedIPs 树、对等体列表实现无锁读取和高并发。5.1 核心概念读端通过RcuReadLock提升 IRQL 到 DPC 级别禁止抢占和中断从而保护读取过程。写端使用RcuAssignPointer原子替换指针释放写屏障然后调用RcuCall或RcuFree延迟释放旧数据。同步点RcuSynchronize等待所有 CPU 至少经历一次上下文切换确保所有读端完成。5.2 实现细节RcuReadLock调用KeRaiseIrqlToDpcLevelRcuReadUnlock恢复 IRQL。延迟释放通过RCU_CALLBACK回调实现回调在专用内核线程中执行该线程等待所有 CPU 完成 DPC 后再执行回调。提供RcuCall执行函数和RcuFree释放内存两种回调类型。5.3 使用场景AllowedIPs 节点更新和释放。对等体列表和端点更新。套接字指针替换Sock4/Sock6。密钥对替换CurrentKeypair、NextKeypair。6. 内存管理memory.c / memory.h6.1 内存分配器使用ExAllocatePoolUninitialized/ExAllocatePoolZero分配非分页池NonPagedPool。提供辅助宏MemAllocate、MemAllocateAndZero、MemAllocateArray、MemAllocateArrayAndZero。敏感数据使用MemFreeSensitive在释放前清零。6.2 NET_BUFFER 缓存池为减少频繁分配预创建多种大小的NET_BUFFER_LIST_POOL和NET_BUFFER_POOL每种大小对应一个缓存池。分配时根据所需大小选择最合适的池若超出最大值则使用通用池LooseNblPool和LooseNbPool。关键函数MemAllocateNetBufferList分配一个 NBL包含所需的头部预留空间和数据空间。MemAllocateNetBufferListWithClonedGeometry克隆一个 NBL 的结构MDL 布局并增加额外空间用于发送路径中为每个包添加 WireGuard 头部。MemFreeNetBufferList释放 NBL若使用缓存池则归还到池中。6.3 MDL 操作MemAllocateDataAndMdlChain分配内存并创建 MDL 链。MemFreeDataAndMdlChain释放 MDL 链及关联内存。MemCopyFromMdl从 MDL 链复制数据到线性缓冲区用于某些情况。7. 对等体管理peer.c / peer.h7.1 对等体对象WG_PEER包含以下关键成员设备指针Device。发送/接收队列TxQueue、RxQueue。暂存队列StagedPacketQueue。引用计数Refcount和撤销保护InUse。密钥对Keypairs。端点地址Endpoint及锁。握手状态Handshake。Cookie 缓存LatestCookie。定时器。统计信息RxBytes、TxBytes。链表节点PeerList、AllowedIpsList。7.2 创建与删除PeerCreate分配并初始化对等体添加到设备列表、公钥哈希表和 AllowedIPs 表由外部调用插入 IP 前缀。PeerRemove从设备移除清理相关资源AllowedIPs、哈希表、密钥对等待引用计数归零后释放。PeerRemoveAll移除设备所有对等体。7.3 引用计数Kref64 位原子引用计数用于管理对等体的生命周期。PeerGet/PeerPut/PeerGetMaybeZero管理引用计数在 Put 时若计数降为 0则触发 RCU 延迟释放。7.4 哈希表查找对等体可通过公钥PubkeyHashtableLookup或索引IndexHashtableLookup快速查找。8. 对等体哈希表peerlookup.c / peerlookup.h8.1 公钥哈希表PUBKEY_HASHTABLE使用 SipHash 对公钥哈希分布到 2^112048个桶中。PubkeyHashtableAdd、Remove、Lookup操作支持 RCU 读取。8.2 索引哈希表INDEX_HASHTABLE用于快速根据握手索引SenderIndex或ReceiverIndex查找对等体。索引是 32 位随机数插入时循环尝试直到找到未使用的索引。支持两种类型INDEX_HASHTABLE_HANDSHAKE握手条目和INDEX_HASHTABLE_KEYPAIR密钥对条目。插入/删除/替换操作均使用自旋锁保护。9. 总结第三篇介绍了 WireGuardNT 驱动中关键的辅助模块AllowedIPs实现了高效的路由表查找支持动态更新。Cookie 和速率限制器共同防御 DoS 攻击保护握手过程。RCU提供了无锁并发读的同步机制保证高性能。内存池优化了数据包分配的效率。对等体管理和哈希表提供了对等体的生命周期管理和快速查找。这些模块虽然不直接处理数据包加密但为数据面的正确性和性能提供了坚实基础。下一篇将介绍日志、调试支持和自测试框架。
返回列表