返回

UDP-TCP校验和增量计算详解

问题。

网络校验和增量计算详解

适用于 NAT、隧道加密、FPGA 网络加速等场景

1. 背景

在网络设备(如 NAT 网关、VPN 隧道、FPGA 加速卡)中,经常需要修改 IP 地址。修改后,相关的校验和必须更新:

校验和类型覆盖范围改 IP 后需要更新吗
IP Header Checksum仅 IP 头(20 字节)✅ 必须
UDP Checksum伪头部 + UDP 头 + 载荷✅ 必须
TCP Checksum伪头部 + TCP 头 + 载荷✅ 必须

问题:如果重新遍历整个包来计算校验和,对于大包分片场景效率很低。

解决方案增量计算——只根据改变的字段更新校验和,无需遍历全包。


2. 反码加法原理

IP/TCP/UDP 校验和使用 16-bit 反码加法(One’s Complement Arithmetic)

2.1 计算规则

1
2
3
4
1. 把数据分成 16-bit 块
2. 逐块相加(32-bit 累加)
3. 进位回卷:高 16-bit 加回低 16-bit
4. 最后取反(~)得到校验和

2.2 计算示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
计算 0x1234 + 0xFEDC 的反码和:

  0x1234
+ 0xFEDC
─────────
 0x11110  ← 产生进位(超过 16-bit)

进位回卷:
  0x1110 + 0x0001 = 0x1111

取反得校验和:
  ~0x1111 = 0xEEEE

2.3 关键性质

反码加法有一个重要性质:可逆且结合

1
2
3
4
5
6
7
8
9
如果:Checksum = ~(A + B + C + D)

那么:~Checksum = A + B + C + D(中间和)

改变 A → A' 后:
  新的中间和 = A' + B + C + D
             = (A + B + C + D) - A + A'
             = ~Checksum - A + A'
             = ~Checksum + ~A + A'  (反码中 -X = ~X + 进位调整)

3. 增量计算公式

3.1 正向增量(加密端 / NAT 出向)

场景:将 IP 地址从 OLD_IP 改为 NEW_IP

1
2
3
4
5
6
公式:
  S = ~C_old                          // Step 1: 取反旧校验和
  S = S + ~OLD_IP[31:16] + ~OLD_IP[15:0]  // Step 2: 减去旧值
  S = S + NEW_IP[31:16] + NEW_IP[15:0]    // Step 3: 加上新值
  S = fold(S)                         // Step 4: 进位回卷
  C_new = ~S                          // Step 5: 取反得新校验和

进位回卷函数

1
fold(S) = (S[15:0] + S[31:16]),重复直到 S ≤ 0xFFFF

3.2 反向增量(解密端 / NAT 入向)

场景:将 IP 地址从 NEW_IP 改回 OLD_IP(与正向完全对称)

1
2
3
4
5
6
公式:
  S = ~C_encrypted                    // Step 1: 取反加密端的校验和
  S = S + ~NEW_IP[31:16] + ~NEW_IP[15:0]  // Step 2: 减去新值
  S = S + OLD_IP[31:16] + OLD_IP[15:0]    // Step 3: 加上旧值
  S = fold(S)                         // Step 4: 进位回卷
  C_original = ~S                     // Step 5: 取反得原始校验和

注意:反向增量和正向增量是完全对称的操作,执行后可恢复原始校验和。


4. 完整计算示例

4.1 正向增量示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
原始数据:
  Src IP = 192.168.1.100 = 0xC0A8_0164
  UDP Checksum = 0x1234

修改后:
  Src IP = 172.16.0.50 = 0xAC10_0032

计算过程:
  Step 1: S = ~0x1234 = 0xEDCB

  Step 2: 减去旧 IP(加反码)
          S = 0xEDCB + ~0xC0A8 + ~0x0164
            = 0xEDCB + 0x3F57 + 0xFE9B
            = 0x2CBBD
          回卷: 0xCBBD + 0x0002 = 0xCBBF

  Step 3: 加上新 IP
          S = 0xCBBF + 0xAC10 + 0x0032
            = 0x17801
          回卷: 0x7801 + 0x0001 = 0x7802

  Step 4: 新校验和
          C_new = ~0x7802 = 0x87FD

4.2 反向增量示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
加密端数据:
  Src IP = 172.16.0.50 = 0xAC10_0032
  UDP Checksum = 0x87FD(加密端计算的结果)

还原后:
  Src IP = 192.168.1.100 = 0xC0A8_0164

计算过程:
  Step 1: S = ~0x87FD = 0x7802

  Step 2: 减去新 IP
          S = 0x7802 + ~0xAC10 + ~0x0032
            = 0x7802 + 0x53EF + 0xFFCD
            = 0x1C9BE
          回卷: 0xC9BE + 0x0001 = 0xC9BF

  Step 3: 加上旧 IP
          S = 0xC9BF + 0xC0A8 + 0x0164
            = 0x18BCB
          回卷: 0x8BCB + 0x0001 = 0x8BCC
          (继续检查,不需要再回卷)

  Step 4: 原始校验和
          C_original = ~0xEDCB = 0x1234 ✓

结果与原始校验和一致!


5. FPGA Verilog 实现

5.1 增量更新模块

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module checksum_incremental_update (
    input  wire [15 :0]             i_old_checksum                      ,
    input  wire [31 :0]             i_old_ip                            ,
    input  wire [31 :0]             i_new_ip                            ,
    output wire [15 :0]             o_new_checksum
);

    // 中间累加和
    wire    [31 :0]                 w_sum                               ;
    // 进位折叠
    wire    [16 :0]                 w_fold_1                            ;
    wire    [16 :0]                 w_fold_2                            ;

    // Step 1-3: 取反 + 减旧 + 加新
    assign w_sum = {16'h0, ~i_old_checksum}
                 + {16'h0, ~i_old_ip[31:16]}
                 + {16'h0, ~i_old_ip[15:0]}
                 + {16'h0,  i_new_ip[31:16]}
                 + {16'h0,  i_new_ip[15:0]} ;

    // Step 4: 进位回卷(两次确保完全折叠)
    assign w_fold_1 = {1'b0, w_sum[15:0]} + {1'b0, w_sum[31:16]} ;
    assign w_fold_2 = {1'b0, w_fold_1[15:0]} + {15'h0, w_fold_1[16]} ;

    // Step 5: 取反得校验和
    assign o_new_checksum = ~w_fold_2[15:0] ;

endmodule

5.2 同时更新 Src IP 和 Dst IP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
module checksum_update_dual_ip (
    input  wire [15 :0]             i_old_checksum                      ,
    input  wire [31 :0]             i_old_src_ip                        ,
    input  wire [31 :0]             i_old_dst_ip                        ,
    input  wire [31 :0]             i_new_src_ip                        ,
    input  wire [31 :0]             i_new_dst_ip                        ,
    output wire [15 :0]             o_new_checksum
);

    wire    [31 :0]                 w_sum                               ;
    wire    [16 :0]                 w_fold_1                            ;
    wire    [16 :0]                 w_fold_2                            ;
    wire    [16 :0]                 w_fold_3                            ;

    assign w_sum = {16'h0, ~i_old_checksum}
                 // 减旧 Src IP
                 + {16'h0, ~i_old_src_ip[31:16]}
                 + {16'h0, ~i_old_src_ip[15:0]}
                 // 减旧 Dst IP
                 + {16'h0, ~i_old_dst_ip[31:16]}
                 + {16'h0, ~i_old_dst_ip[15:0]}
                 // 加新 Src IP
                 + {16'h0,  i_new_src_ip[31:16]}
                 + {16'h0,  i_new_src_ip[15:0]}
                 // 加新 Dst IP
                 + {16'h0,  i_new_dst_ip[31:16]}
                 + {16'h0,  i_new_dst_ip[15:0]} ;

    // 多次折叠确保溢出处理
    assign w_fold_1 = {1'b0, w_sum[15:0]} + {1'b0, w_sum[31:16]} ;
    assign w_fold_2 = {1'b0, w_fold_1[15:0]} + {15'h0, w_fold_1[16]} ;
    assign w_fold_3 = {1'b0, w_fold_2[15:0]} + {15'h0, w_fold_2[16]} ;

    assign o_new_checksum = ~w_fold_3[15:0] ;

endmodule

6. 应用场景

6.1 NAT 网关

1
2
3
4
5
6
7
8
9
出向(LAN → WAN):
  改 Src IP: 192.168.1.x → 公网 IP
  改 Src Port(可选)
  正向增量更新 UDP/TCP Checksum

入向(WAN → LAN):
  改 Dst IP: 公网 IP → 192.168.1.x
  改 Dst Port(可选)
  正向增量更新(或理解为"反向还原")

6.2 VPN 隧道加密设备

1
2
3
4
5
6
7
8
9
加密端:
  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
  │ 收到原始包  │ ──► │ 改 IP 地址  │ ──► │ 更新校验和  │ ──► 加密
  └─────────────┘     └─────────────┘     └─────────────┘

解密端:
  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
  │  解密密文   │ ──► │ 还原 IP 地址│ ──► │ 反向更新校验│ ──► 原始包
  └─────────────┘     └─────────────┘     └─────────────┘

6.3 处理顺序

加密端

  1. 修改 IP 地址
  2. 增量更新 IP Header Checksum(仅覆盖 IP 头)
  3. 增量更新 UDP/TCP Checksum(伪头部含 IP)
  4. 加密 IP 载荷

解密端

  1. 解密 IP 载荷
  2. 还原 IP 地址
  3. 反向更新 IP Header Checksum
  4. 反向更新 UDP/TCP Checksum

7. 特殊情况处理

7.1 UDP 校验和为 0

在 IPv4 中,UDP Checksum = 0 表示"不校验"。但通过计算可能得到 0xFFFF(取反后为 0x0000),需要特殊处理:

1
2
// 计算结果为 0 时,用 0xFFFF 表示
assign o_udp_checksum = (w_result == 16'h0000) ? 16'hFFFF : w_result ;

7.2 IP 分片

  • 第一个分片:有 UDP/TCP 头,需要更新对应校验和
  • 后续分片:无 UDP/TCP 头,只更新 IP Header Checksum
1
2
3
分片 1: [IP Hdr][UDP Hdr][Payload...]   更新 IP Cksum + UDP Cksum
分片 2: [IP Hdr][Payload...]           只更新 IP Cksum
分片 3: [IP Hdr][Payload...]           只更新 IP Cksum

8. 性能对比

方法时间复杂度FPGA 资源延迟
完整计算O(N),N=包长需缓存全包
增量计算O(1)几个加法器极低(1-2 拍)

结论:增量计算是 FPGA 网络处理的标准做法,性能远优于完整计算。


9. 总结

项目正向增量反向增量
场景修改字段后更新校验和还原字段后恢复校验和
公式~(~C + ~OLD + NEW)~(~C + ~NEW + OLD)
对称性正向和反向完全对称执行后恢复原值
实现纯组合逻辑纯组合逻辑

掌握增量计算,是 FPGA 网络处理的基本功!

🄯 2025 - 2026 DeerStar的博客· 0Days
共书写了50.5k字·共 14篇文章
本站总访问量 · 访客数

DeerStar的一些笔记
使用 Hugo 构建
主题 Stack ModIce Year 设计
🄯 Licensed Under CC BY-NC-SA 4.0