首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >校验和增量计算方法(RFC 1624)

校验和增量计算方法(RFC 1624)

作者头像
程序熵
发布2025-11-13 19:35:55
发布2025-11-13 19:35:55
2420
举报
文章被收录于专栏:技术汇技术汇

问题

如果修改了数据包 tcp 的目的端口号,也修改了对应 ipv4 头的目的 ip 地址,如何重新计算 tcp 头的 checksum ?

回答

当同时修改 TCP 目的端口号IPv4 头中的目的 IP 地址 时(例如在 NAT 或负载均衡场景中),由于 TCP 校验和(checksum)的计算包含了 伪头部(Pseudo Header),而伪头部中包含了源 IP、目的 IP、协议号和 TCP 长度,因此这两个字段的修改都会影响最终的 TCP 校验和。

此时,不能再仅对端口做简单的增量更新,而必须考虑 IP 地址变化带来的影响。幸运的是,可以使用 RFC 1624 的增量校验和思想,对两个变化进行 复合增量更新,而无需重新计算整个 TCP 数据的和。

✅ 解决方案:复合增量校验和更新(基于 RFC 1624)

1. 理解 TCP 校验和的组成

TCP 校验和计算基于以下数据的反码和(one's complement sum):

伪头部(Pseudo Header)(12 字节):•源 IP 地址(4 字节)•目的 IP 地址(4 字节) ← 被修改•零字节(1 字节)•协议号(1 字节,TCP 为 6)•TCP 长度(2 字节)•TCP 头部(含端口号等)•源端口(2 字节)•目的端口(2 字节) ← 被修改•其他字段(序列号、确认号、标志位等)•TCP 数据

最终校验和是使得所有这些 16 位字之和为 0xFFFF 的值。


2. 核心思想:增量更新

如果原始校验和为 C,我们修改了两个 16 位字段(或可表示为 16 位和的字段),则新的校验和为:

代码语言:javascript
复制

其中 Δ 是所有修改字段的 反码差值之和

但由于 IP 地址是 32 位,需拆分为两个 16 位字段处理。

✅ 分步计算步骤

假设:

•原目的 IP:old_dst_ip = A1A2 A3A4(十六进制,网络字节序)•新目的 IP:new_dst_ip = B1B2 B3B4•原目的端口:old_dst_port(16 位)•新目的端口:new_dst_port(16 位)•原 TCP 校验和:tcp_checksum(16 位)

步骤 1:拆分 IP 地址为两个 16 位字段

在伪头部中,目的 IP 地址是 32 位,按网络字节序排列,可视为两个 16 位数:

•高 16 位:old_high = (A1A2)new_high = (B1B2)•低 16 位:old_low = (A3A4)new_low = (B3B4)

步骤 2:计算每个修改字段的“反码差值”

根据 RFC 1624,每个字段从 old 变为 new,对校验和的贡献是 (~old + new)

因此总增量为:

代码语言:javascript
复制

注意:~old 是按位取反,所有运算为 反码算术加法(需处理进位)。

步骤 3:更新校验和
代码语言:javascript
复制

然后进行 回卷进位(end-around carry) 处理:

代码语言:javascript
复制
// 用 32 位累加器处理进位
uint32_t sum = (uint32_t)tcp_checksum;
sum += (uint32_t)(~old_high) + (uint32_t)new_high;
sum += (uint32_t)(~old_low)  + (uint32_t)new_low;
sum += (uint32_t)(~old_dst_port) + (uint32_t)new_dst_port;
// 处理回卷进位:将高 16 位加到低 16 位,直到无进位
while (sum >> 16) {
    sum = (sum & 0xFFFF) + (sum >> 16);
}
uint16_t new_tcp_checksum = (uint16_t)sum;
步骤 4:更新 IP 头校验和(额外)

别忘了,IPv4 头校验和也依赖于目的 IP 地址。IP 头校验和只覆盖 IP 头本身,不包含传输层数据。

IP 头校验和也可以增量更新:

代码语言:javascript
复制
// IP 校验和更新(假设原 IP 校验和为 ip_checksum)
uint32_t ip_sum = (uint32_t)ip_checksum + (uint32_t)(~old_dst_ip_high) + (uint32_t)new_dst_ip_high
                                + (uint32_t)(~old_dst_ip_low)  + (uint32_t)new_dst_ip_low;
while (ip_sum >> 16) {
    ip_sum = (ip_sum & 0xFFFF) + (ip_sum >> 16);
}
ip_header->checksum = (uint16_t)ip_sum;

✅ 示例(简化数值)

假设:

•原目的 IP:192.168.1.10xC0A80101 → 高 0xC0A8,低 0x0101•新目的 IP:10.0.0.10x0A000001 → 高 0x0A00,低 0x0001•原目的端口:50000x1388•新目的端口:800x0050•原 TCP 校验和:0x1234

计算:

代码语言:javascript
复制
sum = 0x1234
    + (~0xC0A8 + 0x0A00)   // IP 高 16 位
    + (~0x0101 + 0x0001)   // IP 低 16 位
    + (~0x1388 + 0x0050)   // 端口

即:

代码语言:javascript
复制
sum = 0x1234
    + (0x3F57 + 0x0A00)   // ~0xC0A8 = 0x3F57
    + (0xFEFE + 0x0001)   // ~0x0101 = 0xFEFE
    + (0xEC77 + 0x0050)   // ~0x1388 = 0xEC77
sum = 0x1234 + 0x4957 + 0xFEFF + 0xEC87
    = 0x1234 + 0x4957 = 0x5B8B
    + 0xFEFF = 0x15B8A → 回卷: 0x5B8A + 1 = 0x5B8B
    + 0xEC87 = 0x14812 → 回卷: 0x4812 + 1 = 0x4813

所以新 TCP 校验和为 0x4813


✅ 总结

当同时修改 TCP 目的端口IPv4 目的 IP 地址 时,重新计算 TCP 校验和的方法如下:

1.将目的 IP 地址拆分为两个 16 位字段(高 16 位和低 16 位)。2.对每个被修改的 16 位字段(IP 高、IP 低、目的端口),计算增量:(~old_value + new_value)。3.将这些增量与原 TCP 校验和相加。4.对结果进行 回卷进位处理,直到结果在 16 位范围内。5.得到的新值即为更新后的 TCP 校验和。6.额外:使用类似方法更新 IPv4 头校验和(仅针对 IP 头字段)。

这种方法正是 RFC 1624 所倡导的 增量校验和(Incremental Checksum) 的典型应用。


对比全量重算法

对比维度

RFC 1624 增量校验和更新法

DPDK rte_ipv4_udptcp_cksum 重算法

性能(小包)

⭐⭐⭐⭐⭐ 极高 O(1)仅需计算修改字段的补码差值,不访问 payload

⭐⭐⭐⭐ 中高需遍历 TCP 首部 + 整个 payload,但 DPDK 使用 SIMD 加速(如 SSE/AVX),对小包很快

性能(大包)

⭐⭐⭐⭐⭐ 几乎不变仍 O(1)

⭐⭐ 较低与 payload 大小成正比 O(n),大包(如 64KB jumbo frame)需大量内存访问和求和,开销显著上升

适用场景

✅ 单字段或少量字段修改(如 port、TTL、DSCP 等字段的修改)

✅ 任意修改(IP、端口、payload 等)通用性强,适合复杂处理

实现复杂度

❌ 高,需精确处理反码、进位、字节序、奇数字节对齐等;易出错

✅ 低,调用标准 API,逻辑清晰,DPDK 已封装细节

可维护性与安全性

❌ 差,代码晦涩,调试困难;若旧字段读错或旧校验和错误,结果错误且难以察觉

✅ 好,基于原始数据重新计算,不依赖中间状态,更可靠

健壮性

❌ 弱,依赖原始校验和正确性和字段值准确性

✅ 强,可验证原始报文是否合法(通过重算后比对)

硬件加速支持

❌ 无,纯逻辑运算,无法利用 SIMD

✅ 强,rte_raw_cksum 可使用 SIMD 指令(如 sse, avx)优化求和过程

内存访问开销

✅ 极低,仅访问修改字段和旧校验和

⚠️ 高,需访问:IP 伪首部 + TCP 首部 + 整个 TCP payload大包时可能引起 cache miss 和内存带宽瓶颈

缓存局部性

✅ 好,只访问极小区域

⚠️ 差,大 payload 可能超出 L1/L2 缓存,导致性能下降

校验和验证能力

❌ 无,不能用于验证原始报文完整性

✅ 有,可用于校验接收到的报文是否出错

参考文档

•RFC 1624: Computation of the Internet Checksum via Incremental Update•Intel DPDK Programmer’s Guide

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序熵 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 回答
    • ✅ 解决方案:复合增量校验和更新(基于 RFC 1624)
      • 1. 理解 TCP 校验和的组成
      • 2. 核心思想:增量更新
    • ✅ 分步计算步骤
      • 步骤 1:拆分 IP 地址为两个 16 位字段
      • 步骤 2:计算每个修改字段的“反码差值”
      • 步骤 3:更新校验和
      • 步骤 4:更新 IP 头校验和(额外)
    • ✅ 示例(简化数值)
    • ✅ 总结
    • 对比全量重算法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档