土法炼钢兴趣小组的算法知识备份

【WireGuard】内核代码路径:从 wg_xmit 到加解密 worker

文章导航

分类入口
networkkernel
标签入口
#wireguard#linux-kernel#allowedips#netlink#noise#datapath#linux-6.6

目录

【WireGuard】内核代码路径:从 wg_xmit 到加解密 worker

协议纸面落进 Linux 的位置是 drivers/net/wireguard/。本文锚定 Linux 6.6 LTS 源码(从 torvalds/linuxv6.6 树核对),按发送、接收、握手、配置四条线把关键函数钉住。全景叙事与更多 Mermaid 总图见已有 WireGuard 内部实现;本文补带行号的阅读地图与 6.6 常量。

一、目录与体量(Linux 6.6)

v6.6 标签下若干核心 .c 的行数(wc -l,本机拉取核对):

文件 行数 职责
noise.c 861 握手、密钥派生、keypair
netlink.c 644 Generic Netlink 配置
receive.c 586 UDP 接收、解密路径、NAPI
device.c 479 net_devicewg_xmit
send.c 413 发送、加密 worker、握手发送
allowedips.c 389 IPv4/IPv6 trie
timers.c 243 rekey / keepalive / zeroing
cookie.c 236 MAC1/MAC2 / cookie reply

另有 socket.cpeer.cratelimiter.cqueueing.* 等。体量仍然远小于完整 IPsec+IKE 栈;行数会随主线演进,引用时写清版本。

二、发送:ndo_start_xmit → AllowedIPs → 队列

device.cndo_start_xmit 指到 wg_xmit(约第 239 行 .ndo_start_xmit = wg_xmit)。

wg_xmit(约第 137 行起)核心逻辑:

  1. 校验内层是 IP/IPv6(否则 -EPROTONOSUPPORT)。
  2. peer = wg_allowedips_lookup_dst(&wg->peer_allowedips, skb) —— 目的 IP 选 peer
  3. 无 peer → -ENOKEY,并对 IPv4/IPv6 打 debug,走 ICMP 错误路径(err_icmp)。
  4. endpoint 未配置且从未学到 → -EDESTADDRREQ
  5. 处理 GSO 分段后,把 skb 交给后续发送/加密路径(send.c)。

AllowedIPs 查找(allowedips.c 约 356–372 行):

// Linux 6.6 drivers/net/wireguard/allowedips.c
struct wg_peer *wg_allowedips_lookup_dst(struct allowedips *table,
                                         struct sk_buff *skb)
{
        if (skb->protocol == htons(ETH_P_IP))
                return lookup(table->root4, 32, &ip_hdr(skb)->daddr);
        else if (skb->protocol == htons(ETH_P_IPV6))
                return lookup(table->root6, 128, &ipv6_hdr(skb)->daddr);
        return NULL;
}

入方向对称函数是 wg_allowedips_lookup_src(看内层地址)。这就是 cryptokey routing 在代码里的形状:同一张 trie,出看 dst、进看 src。

flowchart TD
  A["IP stack -> wg0"] --> B["wg_xmit"]
  B --> C["wg_allowedips_lookup_dst"]
  C -->|miss| D["-ENOKEY / ICMP"]
  C -->|hit| E["peer staged queue"]
  E --> F["encrypt_queue worker"]
  F --> G["UDP to endpoint"]

三、加密 / 解密 worker 与保序

device.c 在接口创建时初始化并行队列(约 353–358 行):

声明见 queueing.h。设计意图(与 NDSS 论文系统部分、站内 89 文一致):

send.c 中在发送路径检查 REKEY_AFTER_MESSAGES、密钥出生时间等,决定是否发起新握手(约 132 行附近、365 行附近对 REJECT_AFTER_TIME 的引用)。具体阈值常量见下一节。

四、messages.h:协议常量的内核锚点

Linux 6.6 messages.h 中与运维/协议相关的枚举(节选):

enum limits {
        REKEY_AFTER_MESSAGES = 1ULL << 60,
        REJECT_AFTER_MESSAGES = U64_MAX - COUNTER_WINDOW_SIZE - 1,
        REKEY_TIMEOUT = 5,
        REKEY_AFTER_TIME = 120,
        REJECT_AFTER_TIME = 180,
        KEEPALIVE_TIMEOUT = 10,
        /* ... */
};

enum message_type {
        MESSAGE_HANDSHAKE_INITIATION = 1,
        MESSAGE_HANDSHAKE_RESPONSE = 2,
        MESSAGE_HANDSHAKE_COOKIE = 3,
        MESSAGE_DATA = 4
};

timers.c 文件头注释用这些名字描述 keepalive、拒绝寿命、临时密钥清零(REJECT_AFTER_TIME * 3)等行为——读定时器时以注释 + mod_peer_timer 调用点为准,不要凭记忆换算。

Cookie 相关:COOKIE_SECRET_MAX_AGE = 2 * 60,与官方「两分钟轮换 cookie secret」一致。

五、握手代码入口

创建 initiation:noise.cwg_noise_handshake_create_initiation(约第 517 行)。
send.c 约第 34 行在需要时调用它组装包并送出。

接收路径在 receive.c 分发 type 1/2/3/4:握手进 noise 状态机;data 进解密与 counter 校验;负载下若需要则走 cookie.c。站内 89 文对 initiation/response 的逐步注释更细,本文不重复公式——协议公式以 协议篇 与官方 protocol 页为准。

三密钥对轮换(current / previous / next)在 noise.c 的 keypair 管理里;寿命与 REJECT_AFTER_TIMEREKEY_AFTER_TIME 联动。读代码时抓住:新旧密钥短暂共存是为了 UDP 乱序与 rekey 竞态,不是长期双会话。

用户态 wg(8) 不直接 ioctl 私有结构,而是通过 Generic Netlink 与 netlink.c 对话(Linux 6.6 该文件约 644 行)。配置项映射到:

这解释了为何 wg show / wg setip link 分工清晰:ip 管地址与路由,wg 管 cryptokey routing 表。运维篇用 netns 实验展示二者必须同时正确。

七、和用户态实现的关系

读内核代码时不要假设 Go 实现行号对应;协议必须一致,队列与 NAPI 细节以 C 模块为准。

八、建议阅读顺序(源码)

  1. messages.h —— 常量与消息结构
  2. device.c::wg_xmit + allowedips.c —— cryptokey routing
  3. send.c / receive.c 的 worker 入口 —— 数据面
  4. noise.c 握手创建/消费 —— 控制面
  5. timers.c + cookie.c —— 寿命与 DoS
  6. netlink.c —— 配置如何进内核

读完后回到 内部实现文 第三节对照总图,或进入 运维篇 看这些路径在 wg show 里暴露为什么字段。

参考资料

源码

论文 / 官方

站内

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-20 · network

【网络工程】WireGuard 内部实现:Cryptokey Routing、Noise IK 握手与内核数据路径

WireGuard 用不到 4000 行内核代码替代了 IPSec/IKE 数十万行的协议栈——这不是少写功能,而是用固定密码学原语、固定握手模式和 cryptokey routing 这三个设计决定,换掉了证书体系、协商机和 SPD/SAD 分离。本文钻进源码和协议握手过程,逐层拆解 cryptokey routing 的双向执行、Noise IKpsk2 的 1.5 RTT 握手(含四次 ECDH 的安全属性贡献)、内核多核队列架构、timer 状态机、cookie DoS 防御和三密钥对轮换。


By .