教程 19:从 ip_chksum_pseudo() 到 netif->linkoutput()——lwIP 校验和、Checksum Offload 与驱动边界
摘要:沿 UDP/TCP 的真实收发路径解释 lwIP Internet checksum、pseudo header、per-netif checksum 控制,并明确软件校验和与 MAC/DMA 硬件卸载之间的驱动职责边界。
[TOC]
Checksum(校验和)用于让接收方发现报文头部或 payload 在传输过程中发生的比特错误。lwIP 的 IPv4、TCP、UDP、ICMP/ICMPv6 会在不同层生成或验证各自的 Internet checksum;**Checksum Offload(校验和卸载)**则表示把其中一部分工作从 CPU/lwIP Core 交给 NIC(Network Interface Controller,网卡控制器)、Ethernet MAC(Media Access Control,媒体访问控制)或 DMA(Direct Memory Access,直接内存访问)hardware 完成。
标题中的 ip_chksum_pseudo() 对应 TCP/UDP 等 upper-layer checksum 使用的 pseudo header(伪首部):它不是线上真实存在的一段 header,而是把 source/destination address、协议号/Next Header 和长度等网络层(L3)信息加入 checksum 运算,从而让传输层(L4)checksum 也覆盖关键网络层端点信息。S1S4S5
0. 进入源码前:先把软件 checksum 与 hardware offload 分开
推荐资料只承担标准算法和平台对照,不替代正文:
- RFC 1071 — Computing the Internet Checksum:用于 one’s-complement(反码)加法、carry folding(进位回卷)等算法基础。S3
- lwIP
opt.hchecksum options:确认CHECKSUM_GEN_*、CHECKSUM_CHECK_*、LWIP_CHECKSUM_CTRL_PER_NETIF等配置契约。S8 lwIP Optimization Hints 还说明LWIP_CHKSUM可以由 Port 覆盖成更快的软件实现;这仍然是 software checksum optimization,不等于 MAC/DMA hardware offload。S9 - Linux Kernel — Checksum Offloads:只作为“成熟 OS 如何定义 stack↔NIC offload contract”的对照,不把 Linux metadata 模型套到 lwIP。S7
- Wireshark CaptureSetup — Offloading:解释为什么 Host 本机 TX 抓包可能在硬件补齐 checksum 之前被捕获。S6
Internet checksum 的核心运算可以先建立一个最小模型:把数据按 16-bit word 做 one’s-complement 加法,进位回卷到低位,最后按位取反。lwIP 的 LWIP_CHKSUM()/inet_chksum_*() 会把这套运算应用到连续 buffer 或 pbuf chain(多个 pbuf 串成的报文缓冲链);协议层再决定哪些 header/payload 以及 pseudo header 要参与。S1S3
同时必须区分三个层次:
| 层次 | 谁负责 | 关闭/启用意味着什么 |
|---|---|---|
| lwIP software checksum | Core 中的 CHECKSUM_GEN_* / CHECKSUM_CHECK_* 路径 |
CPU 生成或验证 checksum |
| optimized software routine | Port 覆盖 LWIP_CHKSUM |
仍然由 CPU/软件计算,只是换成更快实现 |
| MAC/DMA/NIC hardware offload | 具体 Driver + hardware descriptor/config | Core 可以不生成,但 Driver 必须明确把工作交给硬件 |
因此最重要的工程边界是:把 CHECKSUM_GEN_TCP 或 CHECKSUM_GEN_UDP 设为 0,并不会自动打开任何硬件功能。 如果 Driver 没有同时设置对应 MAC/DMA offload,线上报文就可能带着无效 checksum 离开设备。S1S2
当前发送路径先看成:
1 | flowchart LR |
下面先用当前 Unix TAP 配置回答“默认到底是谁算 checksum”,再沿 UDP/TCP 的真实 TX/RX 代码下钻。
1. 当前 Unix TAP 路径默认是谁算 checksum
先从配置事实开始。src/include/lwip/opt.h 与官方 Doxygen 都显示 LWIP_CHECKSUM_CTRL_PER_NETIF 默认是 0;CHECKSUM_GEN_IP/UDP/TCP/ICMP/ICMP6 与对应的 CHECKSUM_CHECK_* 默认都是 1。S1S8
1 |
因此在没有 lwipopts.h 覆盖时,TX 由 lwIP Core 生成常用 L3/L4 checksum,RX 也由 Core 验证。IPv4 header、ICMP/ICMPv6 对应宏同样默认启用。S1
这与当前 Unix TAP 的 Driver 行为匹配。tapif_init() 把 L2 发送函数绑定为 low_level_output():S2
1 | err_t |
这里没有 DMA checksum insertion 或 checksum descriptor metadata。后文会回到这个 Port 边界。
2. 从真实 UDP 发送入口继续:checksum 在进入 IP 层前完成
Stage 5 已介绍 UDP Raw API,Stage 18 已完成 route 与 outgoing netif。这里从已经得到 netif、src_ip、dst_ip 的位置继续。
udp_sendto_if_src() 在默认未启用 checksum-on-copy 的路径下进入 udp_sendto_if_src_chksum() 的主体;该函数为 UDP header 准备空间,最终让 q 表示完整 UDP datagram。S1
继续阅读 udp_sendto_if_src_chksum() 的 header 初始化:S1
1 | udphdr = (struct udp_hdr *)q->payload; |
这里的 0 首先是计算前初始值,后面的分支才决定是否跳过生成。S1S4
继续阅读同一个函数的普通 UDP checksum 分支:S1
1 |
|
这里有三层关键控制:compile-time CHECKSUM_GEN_UDP、可选的 per-netif NETIF_CHECKSUM_GEN_UDP,以及 IPv4 可选 zero checksum 与 IPv6 默认 mandatory checksum 的协议差异。S1S4S5
RFC 768 规定计算结果恰好为 0x0000 时线上写成 0xffff,因为全零在 IPv4 UDP 中表示发送端没有生成 checksum;当前 lwIP 代码与此直接对应。S4
3. ip_chksum_pseudo() 为什么不是只算 UDP/TCP header
udp_sendto_if_src_chksum() 调用的是:
1 | ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len, src_ip, dst_ip); |
进入 ip_chksum_pseudo()。它先根据 destination 的地址族选择 IPv4 或 IPv6 pseudo checksum 实现:S1
1 | u16_t |
Pseudo header 不是线上 TCP/UDP packet 前面真的多出一个 header。它是 仅参与 checksum 运算的逻辑输入。IPv4 UDP 的 pseudo header 至少包含:source address、destination address、protocol 和 UDP length;IPv6 则使用 128-bit source/destination、upper-layer length 与 Next Header。S4S5
它的作用不是代替 IP Header checksum,而是让 TCP/UDP checksum 同时保护一部分关键 L3 端点信息。
因此必须区分:
| checksum | 实际覆盖范围 |
|---|---|
| IPv4 Header checksum | IPv4 header 本身 |
| TCP checksum | pseudo header + TCP header + TCP payload |
| UDP checksum | pseudo header + UDP header + UDP payload |
| ICMPv4 checksum | ICMPv4 message,不带 IPv4 pseudo header |
| ICMPv6 checksum | IPv6 pseudo header + ICMPv6 message |
IPv6 基本 header 本身没有类似 IPv4 Header checksum;RFC 8200 同时规定 UDP over IPv6 默认不能使用 zero checksum,并说明 ICMPv6 也把 IPv6 pseudo header 纳入 checksum。S5
4. 进入 inet_chksum_pseudo():先累计地址,再遍历整个 pbuf chain
IPv4 路径进入 inet_chksum_pseudo()。函数先把 source 和 destination IPv4 address 分成 16-bit word 加到 accumulator,然后进入公共的 inet_cksum_pseudo_base():S1
1 | u16_t |
进入 inet_cksum_pseudo_base()。这里是理解 pbuf chain 与 checksum 关系的关键位置:S1
1 | static u16_t |
这个循环说明 checksum 并不要求 UDP/TCP packet 物理上是一块连续内存。Stage 3 的:
1 | pbuf A -> pbuf B -> pbuf C |
可以直接作为一个逻辑 datagram/segment 被累计。
真正需要处理的是 奇数字节边界。Internet checksum 把连续字节按 16 bit 成对解释;若某个 pbuf 恰好以奇数字节结束,下一个 pbuf 的首字节在逻辑报文里仍要与前一个尾字节配成同一个 16-bit word。所以当前实现通过 swapped 和 SWAP_BYTES_IN_WORD() 保持跨 pbuf 边界的字节配对关系。S1S3
RFC 1071 描述的基础算法就是 16-bit one’s-complement addition,并要求 end-around carry;lwIP 的 FOLD_U32T() 正是在累计过程中把高位 carry 折回低 16 bit。S3
5. 从 ip_chksum_pseudo() 返回 UDP:checksum 完成后才进入 IP 输出
ip_chksum_pseudo() 返回后,udp_sendto_if_src_chksum() 把结果写入 udphdr->chksum,然后继续走本来就存在的 IP 输出链:S1
1 | NETIF_SET_HINTS(netif, &(pcb->netif_hints)); |
因此当前默认 UDP TX 顺序是:
1 | flowchart LR |
L4 checksum 在 IP header 构造之前已经可以完成,是因为 pseudo header 所需的 source/destination/protocol/length 已经由上层参数确定,并不需要先把真实 IP header 放入 pbuf。
6. IPv4 Header checksum 是另一条独立路径
若 destination 是 IPv4,ip_output_if_src() 最终进入 ip4_output_if() 系列。继续阅读 IPv4 header 构造中的 checksum 分支:S1
1 |
|
这条路径只处理 IPv4 header checksum。它不会替代已经在 UDP/TCP 层生成的 transport checksum。
因此一个 IPv4 UDP packet 在 software checksum 全开的情况下至少发生两次不同的 checksum 计算:
1 | UDP layer: |
IPv6 不存在第二项 IPv6 base-header checksum,所以 IPv6 数据面更依赖 TCP/UDP/ICMPv6 自身的 end-to-end checksum。S5
7. TCP TX 使用相同 pseudo-header 机制,但还要处理重传与 checksum-on-copy
TCP 发送最终在 tcp_output_segment() 中把 segment 送入 IP 层。进入该函数准备 checksum 的位置,代码先把 TCP checksum 字段清零:S1
1 | seg->p->payload = seg->tcphdr; |
继续阅读 tcp_output_segment() 的 checksum 分支:S1
1 |
|
默认 LWIP_CHECKSUM_ON_COPY 是 0,因此普通路径直接对完整 pbuf chain 调用 ip_chksum_pseudo()。S1
若项目启用 LWIP_CHECKSUM_ON_COPY=1,payload 在从 application buffer 拷进 pbuf 时就可以同步累计 checksum,最终发送只需要重新计算会变化的 TCP header/pseudo-header 部分,再合并之前保存的 payload checksum。这是 software optimization,不是 hardware offload。S1
两者不要混淆:
| 机制 | 谁计算 | 什么时候计算 |
|---|---|---|
| 普通 software checksum | CPU / lwIP | packet 发送前完整遍历 |
LWIP_CHECKSUM_ON_COPY |
CPU / lwIP | copy payload 时先累计一部分 |
| Hardware checksum offload | MAC/NIC/DMA engine | Driver 提交 descriptor 后、真正上线前 |
8. TX checksum 完成后,Core 最终只把 pbuf 交给 linkoutput
Stage 4 已经讲过 ARP,Stage 15 已经讲过 ND6。无论 IPv4 最终经 etharp_output(),还是 IPv6 经 ethip6_output(),解析出 destination MAC 后都会进入 ethernet_output()。
进入 ethernet_output()。它增加 Ethernet header,填写 EtherType、source MAC 和 destination MAC,然后只有一个真正的发送调用:S1
1 | err_t |
这一接口非常值得注意:
1 | netif->linkoutput(netif, p) |
参数只有 netif 和 pbuf。当前通用 struct pbuf flags 里也没有一个标准化的“这个 packet 需要硬件从 offset X 计算 TCP checksum,并写到 offset Y”的 metadata。S1
所以 lwIP Core 的 per-netif checksum 开关只负责决定 Core 自己算不算,不构成一个完整的通用硬件 descriptor 协议。 真正的硬件 offload 对接仍由具体 Port/Driver 决定。
9. 当前 Unix tapif 为什么没有 hardware checksum offload
回到 tapif_init() 绑定的 low_level_output()。当前实现把整个 pbuf chain copy 到栈上的 buf[1518],然后直接写入 TAP fd:S2
1 | static err_t |
这里没有:
- DMA TX descriptor;
- checksum insertion bit;
- checksum start offset;
- checksum field offset;
- protocol-specific hardware command。
因此当前实验路径的正确模型是:
1 | lwIP Core |
如果仅仅把 CHECKSUM_GEN_UDP/TCP/IP 关掉,却不改变 tapif,得到的不是“开启硬件 offload”,而是 直接把未由软件完成的 checksum 字段交给 TAP。
10. LWIP_CHECKSUM_CTRL_PER_NETIF 解决的是“不同网卡能力不同”
Stage 18 引入 multi-netif 后,一个现实问题出现:
1 | netif0 = MCU Ethernet MAC |
如果只靠全局:
1 |
两张网卡都会失去 lwIP software TCP checksum。这不适合能力不同的接口。
于是 LWIP_CHECKSUM_CTRL_PER_NETIF 提供 runtime netif 粒度控制。netif.h 定义了每种生成/校验 bit:S1
1 |
struct netif 在该配置打开后才真正拥有 chksum_flags:S1
1 |
|
而 netif_add() 初始化接口时默认把这些 bit 全部打开:S1
1 | NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL); |
这表示:开启 per-netif 功能并不会自动关闭软件 checksum。 Driver 或 board port 必须根据硬件实际能力主动修改 chksum_flags。
11. IF__NETIF_CHECKSUM_ENABLED() 如何把 compile-time 与 runtime 两层合起来
真正控制每个 checksum call site 的宏是:S1
1 |
它产生两种不同编译结果。
per-netif 关闭
例如:
1 |
|
当 LWIP_CHECKSUM_CTRL_PER_NETIF=0 时,IF__NETIF_CHECKSUM_ENABLED() 展开为空,所以只要 CHECKSUM_GEN_TCP=1,内部 block 就无条件执行。
per-netif 开启
当 LWIP_CHECKSUM_CTRL_PER_NETIF=1 时,它真正展开成 if (...),同一个 Core binary 才能对不同 netif 采用不同策略。
opt.h 还明确要求:启用 per-netif 控制时,相应 CHECKSUM_GEN_* / CHECKSUM_CHECK_* compile-time 选项本身必须保持启用,否则那段代码已经被预处理器整个裁掉,runtime bit 没有机会重新打开。S1
12. 一个典型的“部分硬件 offload”配置应该怎样理解
下面不是当前 Unix Port 的上游代码,而是 Driver 集成示意。假设某 MCU Ethernet MAC 只负责 TCP/UDP TX checksum 和 TCP/UDP RX verify,但 IPv4 header checksum、ICMP/ICMPv6 仍让 lwIP 软件处理。
首先保留对应 Core 代码,并打开 per-netif 控制:
1 |
随后在该硬件 netif 初始化完成时,才根据真实能力关闭 lwIP 的对应 software bit。示意代码如下:
1 | u16_t checksum_flags; |
此时只建立了一个 Core contract:这张 netif 的 TCP/UDP checksum 不再由 lwIP Core 生成/验证。
还必须同时存在 Driver/hardware contract:Driver 要把相应生成/验证任务明确配置给 MAC/DMA,并正确解释 TX descriptor 或 RX status。前者不能替代后者。
13. TX hardware offload 真正发生在 lwIP Core 之后
典型 MCU Ethernet TX 可以抽象成:
1 | flowchart TD |
图中 C~F 不属于 lwIP Core 的通用实现。不同 MCU/SoC 可能要求:
- Driver 设置 descriptor bit;
- Driver 指定 L3/L4 类型;
- Driver 指定 checksum 起始位置;
- Driver 只支持 IPv4 TCP/UDP,不支持 ICMP;
- Driver 对 IPv6 extension header 有额外限制;
- hardware 完全不支持 chained buffer,Driver 必须先线性化。
这些都是芯片/Driver 契约,不能从 CHECKSUM_GEN_TCP=0 推导出来。
这也是为什么当前通用 netif->linkoutput(netif, p) 只能视为 Port ownership boundary:Core 把最终 L2 frame 交出去,具体怎样映射成 DMA descriptor 由 Port 决定。S1S2
14. RX 方向先看 IPv4 Header checksum
接收路径反过来。Driver 构造 pbuf 后,经 netif->input() 进入 ethernet_input(),再进入 ip4_input()。
继续阅读 ip4_input() 的 IPv4 header verify:S1
1 |
|
若软件验证启用,坏 IPv4 header 在进入 UDP/TCP 之前就被丢弃。
若某硬件已经可靠验证 IPv4 header checksum,并且 Port 为这张 netif 关闭 NETIF_CHECKSUM_CHECK_IP,Core 就跳过这一步。
15. 进入 udp_input():IPv4 zero checksum 与真正的 checksum error 不相同
UDP RX 进入 udp_input() 后,只有 packet 确认是本机接收对象,才进入 checksum verify。继续阅读该分支:S1
1 |
|
在普通 IPv4 UDP 中:
1 | udphdr->chksum == 0 |
表示发送端没有提供 checksum,不等于“checksum 算出来恰好是 0”。后者在线上要编码成 0xffff。S4
IPv6 默认规则不同:zero UDP checksum 不能作为普通数据报的默认合法形式。S5
16. 进入 tcp_input():软件 RX verify 失败后不会继续状态机
TCP RX 的对应逻辑在 tcp_input()。函数在解析 TCP header length、查 PCB、推进状态机之前先验证 checksum:S1
1 |
|
所以 software checksum 验证属于 TCP 状态机之前的输入完整性门槛。坏 checksum packet 不会继续进入 tcp_process()、ACK handling 或 receive queue。
这也意味着,如果关闭 NETIF_CHECKSUM_CHECK_TCP:
Core 不再对这一张
netif的 TCP packet 做第二次软件确认。
因此 Driver/硬件接管不能只是“硬件有 checksum 状态位”,还必须把这个状态可靠地转成 Port 的接收策略。
17. lwIP 当前 pbuf 没有通用的 RX checksum-valid 状态
查看当前 pbuf.h,通用 flags 包括 PBUF_FLAG_PUSH、PBUF_FLAG_IS_CUSTOM、multicast/broadcast 以及 TCP FIN 等,但没有类似 Linux skb->ip_summed 的标准 per-packet checksum-valid metadata。S1
因此在当前通用接口中:
1 | 硬件 RX checksum result |
这不是说所有 Driver 都必须采用“坏包在 Driver 直接 drop”这一种策略,而是说:一旦关闭 Core software verify,就必须由 Port 自己定义并保证等价的错误处理契约。 lwIP 通用 pbuf 不会替 Driver 保存一个跨平台统一的硬件校验结果。
Linux 的 sk_buff 有自己的一套 ip_summed/CHECKSUM_PARTIAL 等 offload contract,但那是 Linux network stack 与 Linux driver 之间的接口,不能直接套成 lwIP pbuf 语义。S7
18. TX generation 与 RX verification 必须分别建立 contract
硬件 TX generation 与 RX verification 是两套独立能力;是否关闭某个 GEN_* 或 CHECK_*,必须分别有对应 MAC/DMA capability 与 Driver descriptor/status 处理作为证据。LWIP_CHECKSUM_CTRL_PER_NETIF 只提供 Core 侧开关,不替硬件声明能力。S1S8
19. Ethernet FCS 不是这里的 Internet checksum
FCS(Frame Check Sequence,帧校验序列)是 Ethernet 二层(L2)frame trailer,通常使用 CRC-32 由 MAC 生成/校验;它与 IPv4/TCP/UDP 使用的 Internet checksum 不是同一种机制。即使 Stage 0 已经介绍过 FCS,这里仍需要重新做最小消歧,因为“网卡硬件校验”很容易把两类机制混在一起。
| 对象 | 所在层 | 典型算法 | lwIP pbuf 是否通常携带 |
|---|---|---|---|
| IPv4 Header checksum | L3 | one’s-complement | 是,位于 IPv4 header |
| TCP/UDP checksum | L4 | pseudo header + one’s-complement | 是,位于 TCP/UDP header |
| ICMP/ICMPv6 checksum | L3 control / upper-layer | Internet checksum | 是 |
| Ethernet FCS | L2 frame trailer | CRC-32 | 通常不作为 lwIP Ethernet pbuf 内容 |
当前 Unix tapif.c 自己已经给出一个很直观的边界提示:它的 buf[1518] 注释写的是 excluding CRC。S2
也就是说当前 netif->linkoutput() 看到的是 Ethernet header + payload,但 Ethernet FCS 仍不属于这条 lwIP Core checksum 主线。
因此:
1 | 关闭 TCP checksum software generation |
和:
1 | 让 MAC 自动生成 Ethernet FCS |
是两件不同的事。
20. Wireshark 为什么会把本机 TX packet 标成 bad checksum
这部分属于 Host/NIC 行为,不是当前 TAP Port 的 lwIP Core 行为。
Wireshark 官方文档说明,在操作系统使用 TX checksum offload 时,抓包点可能位于 NIC 真正填入 checksum 之前。于是抓到的本机 outgoing packet 里 checksum 字段仍是未完成值,Wireshark 会显示 incorrect/partial,但实际线上的 NIC 已经在发送前补好。S6
Linux kernel 的 checksum-offload 文档也明确把 TX offload 定义成 stack 请求 device 在指定 checksum start/offset 上完成 one’s-complement checksum;Driver/NIC 负责兑现该请求。S7
典型关系是:
1 | sequenceDiagram |
但 当前 lwIP Unix TAP 实验不能直接用这个理由解释 bad checksum。当前路径是:
1 | lwIP software checksum |
tapif 没有一个后置 NIC checksum engine 帮它补字段。因此若直接在 lwip0/TAP 路径抓到 UDP/TCP checksum 错误,首先应该检查 lwIP 配置、packet 构造和 Port 修改,而不是先假设“这是 offload 假象”。S1S2
只有抓包位置已经进入 Linux 主机自己的真实物理 NIC TX 路径时,才需要把 Linux/NIC offload 作为解释因素之一。S6S7
21. 在当前 TAP 实验里观察 checksum,证据应该怎样对应源码
当前实验不需要先修改 Driver。只要已有 TAP/Ping/UDP/TCP 路径能运行,就可以在 Host 侧抓当前 TAP frame:
1 | sudo tcpdump -i lwip0 -nn -vv -s 0 -w captures/checksum-stage19.pcap |
随后触发已有 UDP 或 TCP traffic,再用 Wireshark 查看:
1 | IPv4 Header checksum |
观察结果应与源码位置一一对应:
| PCAP 字段 | lwIP 生成/验证位置 |
|---|---|
| IPv4 Header Checksum | ip4_output_if() / ip4_input() |
| UDP Checksum | udp_sendto_if_src_chksum() / udp_input() |
| TCP Checksum | tcp_output_segment() / tcp_input() |
| Ethernet FCS | 不属于当前 TAP pbuf 内容 |
如果 LWIP_CHECKSUM_CTRL_PER_NETIF=0 且 CHECKSUM_GEN_* 仍保持默认 1,当前 TAP packet 应在 write() 之前已经具备由 lwIP 完成的 checksum。S1S2
本篇不声称已经执行上述抓包;命令只是复用 Stage 2/4 已经建立的 TAP 抓包方法,把 PCAP 字段重新连接到 checksum 源码位置。
22. 一个真正的 MCU Driver 接入时,职责应怎样分层
把前面的边界压缩成四层即可:lwIP Core 根据 checksum option 决定是否做软件工作;netif->linkoutput()/RX glue 把 pbuf 交给 Port;Driver 把 frame 映射为 DMA descriptor 并解释 RX status;MAC/DMA engine 最终执行硬件生成或验证。
1 | flowchart LR |
任何一层都不能靠另一层的宏自动替代。
23. 错配的本质是 checksum ownership 没有闭环
最危险的错配只有两类:Core 已停止软件工作但 Driver 没有真正接管;或硬件只覆盖部分协议,却把未覆盖协议的 Core generation/verification 一并关闭。排查时按 Core option → per-netif bit → Driver descriptor/status → 抓包位置检查 ownership 即可。
24. 从 Stage 18 到 Stage 19,packet 的完整路径已经延伸到 Driver 边界
Stage 19 把 Stage 18 的 route → netif → next hop 继续延伸到 software checksum → ethernet_output() → linkoutput → Driver/MAC。由此可以明确区分:ip_chksum_pseudo*() 是 lwIP software checksum;LWIP_CHECKSUM_ON_COPY 是 CPU copy/checksum 合并优化;MAC/DMA checksum offload 则属于 Port/Driver 与硬件的契约。
核心判断是:lwIP checksum option 描述 Core 是否执行软件工作,不描述某块具体 Ethernet MAC 的能力,也不替 Driver 配置硬件。 下一阶段自然进入 pbuf、DMA descriptor、zero-copy 与 TX/RX ring ownership。
资料来源
[S1] lwIP Core checksum、协议输入输出与 netif 源码
- 类型:目标版本上游源码
- 版本:commit
d08f4773edd0182b7910fc8f046eed82ffcd67c9 - 定位:
src/core/inet_chksum.c;src/core/udp.c;src/core/tcp_in.c;src/core/tcp_out.c;src/core/ipv4/ip4.c;src/core/ipv4/icmp.c;src/core/ipv6/icmp6.c;src/core/pbuf.c;src/core/netif.c;src/netif/ethernet.c;src/include/lwip/opt.h、netif.h、pbuf.h - URL/文档:lwIP upstream commit
- 使用位置:UDP/TCP TX/RX、pseudo-header、IPv4 header checksum、per-netif checksum control、
linkoutput边界 - 支撑内容:证明软件 checksum 的真实调用点、
pbuf chain累计方式、compile-time/runtime 控制以及 Core 到 Driver 的函数边界
[S2] lwIP Unix TAP Port 与通用 Ethernet Driver 模板
- 类型:目标版本上游 Port/示例
- 版本:commit
d08f4773edd0182b7910fc8f046eed82ffcd67c9 - 定位:
contrib/ports/unix/port/netif/tapif.c:tapif_init()、low_level_output()、low_level_input();contrib/examples/ethernetif/ethernetif.c - URL/文档:lwIP Unix Port
- 使用位置:“当前 Unix TAP”“Driver boundary”“FCS 边界”“MCU Driver 对照”
- 支撑内容:证明 TAP TX 只是 pbuf copy + fd write,以及通用 Ethernet Port 由
linkoutput接管真实硬件发送
[S3] RFC 1071:Internet Checksum 算法
- 类型:IETF 技术文档
- 版本:RFC 1071,September 1988
- URL/文档:RFC 1071 - Computing the Internet Checksum
- 使用位置:“pbuf chain checksum”“one’s-complement / fold”
- 支撑内容:说明 16-bit one’s-complement sum、end-around carry 以及 checksum 实现的基础性质
[S4] RFC 768:UDP checksum 与 IPv4 zero-checksum 语义
- 类型:IETF 标准
- 版本:RFC 768,August 1980
- URL/文档:RFC 768 - User Datagram Protocol
- 使用位置:“UDP TX”“pseudo header”“UDP RX”
- 支撑内容:定义 UDP pseudo header、computed-zero 写成 all ones,以及 transmitted zero 表示未生成 checksum
[S5] RFC 8200:IPv6 Upper-Layer Checksum
- 类型:IETF Internet Standard
- 版本:RFC 8200,July 2017
- URL/文档:RFC 8200 - Internet Protocol, Version 6 (IPv6) Specification
- 使用位置:“IPv6 UDP mandatory checksum”“IPv6 pseudo header”“ICMPv6”
- 支撑内容:说明 IPv6 upper-layer pseudo header、普通 UDP over IPv6 checksum 默认必需,以及 ICMPv6 checksum 使用 pseudo header
[S6] Wireshark Checksum Offload 抓包说明
- 类型:官方工具文档
- 版本:访问日期 2026-10-02
- URL/文档:Wireshark CaptureSetup - Offloading
- 使用位置:“Wireshark 为什么显示 bad checksum”
- 支撑内容:说明本机 TX 抓包点可能位于 NIC checksum completion 之前,因此会出现 apparent/partial checksum
[S7] Linux Kernel Checksum Offload 接口
- 类型:官方内核文档
- 版本:访问日期 2026-10-02
- URL/文档:Linux Kernel - Checksum Offloads
- 使用位置:“Host/NIC offload 对照”“TX/RX Driver contract”
- 支撑内容:说明 Linux stack 与 NIC/Driver 之间的 checksum offload metadata 与职责,用于和 lwIP 的
pbuf/linkoutput边界做对照
[S8] lwIP 官方 checksum 配置文档
- 类型:upstream 官方配置文档
- 版本:lwIP 2.1.x Doxygen,访问日期 2026-10-03
- URL/文档:lwIP opt.h checksum options
- 使用位置:默认 checksum 配置、per-netif 控制与 Core contract
- 支撑内容:列出
LWIP_CHECKSUM_CTRL_PER_NETIF、CHECKSUM_GEN_*、CHECKSUM_CHECK_*与LWIP_CHECKSUM_ON_COPY的默认配置值
[S9] lwIP 官方 Optimization Hints
- 类型:upstream 官方优化文档
- 版本:lwIP 2.1.x,访问日期 2026-10-03
- URL/文档:lwIP Optimization hints
- 使用位置:软件 checksum 与硬件 offload 的边界说明
- 支撑内容:说明 Port 可以通过
LWIP_CHKSUM替换/优化软件 checksum 实现;该优化入口与 MAC/DMA checksum offload 是不同层次









