各位好,我是 Linux猫。
今天不扯虚的架构图,我们直接把 WireGuard 的内核实现剥开来看看。
很多同学对 VPN 的印象还停留在 OpenVPN 繁琐的证书链、IPsec 动辄几千行的 ipsec.conf,以及用户态到内核态疯狂 read()/write() tun/tap 设备带来的上下文切换开销。
WireGuard 自 Linux 5.6 合并入主线内核(作为 drivers/net/wireguard)以来,彻底重构了这种体验。代码量不到 4000 行,没有庞大的协商状态机,核心机制就两样东西:Cryptokey Routing(加密密钥路由) 与 基于 Noise 协议的固定密码原语。
一、 Cryptokey Routing:路由表与公钥绑定
在传统网络栈里,路由(Routing)和加密(Encryption)是完全割裂的:内核只管把包塞给虚拟网卡(比如 tun0),至于 tun0 背后的用户态守护进程怎么加密、发给谁,内核路由子系统一概不知。
WireGuard 提出了 Cryptokey Routing。它的核心逻辑非常优雅:公钥即 IP 地址的访问控制列表(ACL)。
1. 结构与配置映射
来看一个标准的 wg0.conf:
[Interface]
Address = 10.0.0.1/24
PrivateKey = aaaaaa...
ListenPort = 51820
[Peer]
PublicKey = bbbbbb...
Endpoint = 198.51.100.1:51820
AllowedIPs = 10.0.0.2/32, 192.168.1.0/24
在内核的结构体中(见 drivers/net/wireguard/peer.h),每个 struct wg_peer 对应一个对端,里面维护了公钥、Endpoint 地址,以及一颗基数树(Radix Tree / Trie),用于存储 AllowedIPs。
+--------------------------------------------------------+
| Kernel wg_xmit(skb) |
+--------------------------------------------------------+
|
Lookup dest IP in AllowedIPs
(Trie / Radix Tree Match)
|
v
+--------------------------------------------+
| Matched Peer (Public Key: bbbbbb...) |
+--------------------------------------------+
|
ChaCha20-Poly1305 Encrypt
|
v
+--------------------------------------------+
| Send UDP Packet to Endpoint 198.51.100.1 |
+--------------------------------------------+
2. 双向校验机制
- 发包(Outbound):内核调用
wg_xmit()。从skb(套接字缓冲区)中提取目的 IP,在当前网卡所有 Peer 的AllowedIPsTrie 树中查找。命中 Peer 则使用该 Peer 的当前对称会话密钥加密,打上 UDP 头直接扔给对应 Endpoint;没命中直接丢弃(Drop)。 - 收包(Inbound):收到 UDP 数据包,解密后提取内部原始报文的源 IP。如果解密出来的源 IP 不在该 Peer 的
AllowedIPs列表中,内核直接丢包。这在内核层天然免疫了源 IP 欺骗(IP Spoofing)。
二、 极简密码学设计:抛弃 Cipher Agility
许多传统 VPN 性能差、漏洞多,很大程度归咎于 Cipher Agility(密码算法协商)。为了兼容性,双方握手必须协商支持哪种 AES 模式、哪种 SHA、哪种 DH 曲线,这导致握手延迟高(多次 RTT)且极易遭受降级攻击。
WireGuard 直接做减法,写死了现代高性能密码原语组合(Noise_IK 握手模式):
- Noise Protocol Framework:完成 1-RTT 极速握手。
- Curve25519:用于 ECDH 密钥交换。
- ChaCha20-Poly1305:用于数据流的对称加密与身份认证(AEAD)。在没有 AES-NI 硬件加速的 ARM 或低端设备上,ChaCha20 的软件实现吞吐量远超 AES。
- BLAKE2s:用于哈希与 MAC 计算,速度快于 SHA-3。
- SipHash24:用于防御 Hash-flooding 攻击。
无状态沉默(Silent by Default)
没有被正确公钥加密的握手包或探测包,WireGuard 内核模块不会返回任何响应(甚至连 RST 或 ICMP Port Unreachable 都没有)。用 nmap 扫描一个只开着 WireGuard 端口的服务器,结果只会是 closed/filtered。
三、 深入内核数据路径(Datapath)与性能优化
为什么 WireGuard 吞吐量能轻松跑满 10GbE,且延迟逼近裸线?我们用 perf 追一下内核调用栈就能看明白。
传统 OpenVPN 的流程是:
网卡 -> 内核网络栈 -> tun0 -> 用户态进程 (context switch + memcpy) -> 加密 -> 用户态进程 -> 内核网络栈 -> 网卡。
WireGuard 的流程完全在内核态闭环:
[ Ingress: UDP Packet ]
│
▼
napi_gro_receive()
│
▼
wg_receive() <-- 直接在软中断 (softirq) 流程处理
│
pad_crypto_work() <-- 分发给 multi-queue / per-CPU kworker 并行解密
│
▼
netif_rx() / ip_local_deliver() <-- 解密后还原出内层 skb,直接塞回本地网络栈
并发加密与 Multi-Queue
在多核服务器上,单核处理 ChaCha20 加密会遇到瓶颈。WireGuard 利用 Linux 的 pad_crypto_work 将加密解密任务丢进 kworker。
通过 perf top 观察打满吞吐量时的内核符号:
# Overhead Shared Object Symbol
# ........ ....................... ......................................
45.12% [wireguard] [k] chacha20poly1305_encrypt
12.30% [kernel.kallsyms] [k] fib_table_lookup
8.21% [wireguard] [k] wg_receive
6.04% [kernel.kallsyms] [k] ip_finish_output2
你会发现没有跨越用户态/内核态的 copy_to_user 瓶颈,几乎 100% 的 CPU 周期都切实花在了密码学计算与标准 IP 路由查找上。
四、 快速验证与排查
用系统自带的工具,两行命令就能看清对端状态和握手细节:
# 查看 peer、握手时间与传输流量
$ sudo wg show
interface: wg0
public key: 4L8w.../EQ=
private key: (hidden)
listening port: 51820
peer: bbbbbb...
endpoint: 198.51.100.1:51820
allowed ips: 10.0.0.2/32, 192.168.1.0/24
latest handshake: 1 minute, 12 seconds ago
transfer: 1.42 GiB received, 8.91 GiB sent
如果遇到连通性问题,别急着怪加密,90% 是 路由表冲突 或者 AllowedIPs 配窄了。
排查时,可以开启内核动态调试(Dynamic Debug):
# 打开 WireGuard 的内核 debug 输出
echo "module wireguard +p" | sudo tee /sys/kernel/debug/dynamic_debug/control
# 实时查看握手与路由日志
sudo dmesg -wH | grep wireguard
你会看到类似 wireguard: wg0: Receiving handshake initiation from peer 1... 以及路由丢包的直接报错。
总结
WireGuard 之所以成为现代 VPN 的事实标准,原因就在于它的克制:
- 用 Cryptokey Routing 把加密与 Linux 原生路由表逻辑统一。
- 剔除多余的密码学协商,1-RTT 固化现代算法。
- 纯内核实现 + 极短的代码执行路径,消除上下文切换。
搞系统调优的乐趣就在这儿:真正的优雅不是功能越多越好,而是把不需要的代码全部砍掉。
License: CC BY-NC 4.0
Updated 5 hours ago
Was this article helpful? Give it a like.
0 followers
把想说的写下来而已。
Related Articles
More0 comments


