关于 IPv4 校验和计算,有四个 RFC 可供阅读:
RFC 791、RFC 1071、RFC 1141 和 RFC 1624。
我没有阅读它们,直到我遇到一个非常奇怪的问题,我不打算这样做。但是还有另一个很棒的页面谈论 IPv4 的校验和字段:Wikipedia's IPv4 Header Checksum。按照 Wikipedia 上的示例,我尝试计算校验和:
步骤 1. 计算所有 IPv4 标头字段的反码和:
我们可以将所有这些数字以十六进制或二进制形式相加。这两种方法我都会做:
步骤 1a.1:我将添加前两个字段 (4500 + 062A)。然后,我将第三个字段添加到先前添加的结果 (4B2A+42A1)。从那里开始,我会将下一个字段的值添加到累计总和中。
1 1111 1 1
4500 4B2A 8DCB 10DCC 14FDC 21084 21085 2D12D
062A 42A1 8001 4210 C0A8 0001 C0A8 0003
----- ----- ---- ----- ----- ----- ----- -----
4B2A 8DCB 10DCC 14FDC 21084 21085 2D12D 2D130
步骤 1b.1
In octave:
-------------------
octave:14> hex2dec("4500")+hex2dec("062a")+hex2dec("42a1")+hex2dec("8001")+hex2dec("4210")+hex2dec("c0a8")+hex2dec("0001")+hex2dec("c0a8")+hex2dec("0003")
ans = 184624
octave:15>
octave:15> dec2hex(184624)
ans = 2D130
octave:16>
步骤 1a.2:步骤 1a.1 的加法是一组数字的简单数学加法。另一方面,在补码加法中,我们还需要做一件事。由于结果必须适合 16 位(意味着结果应该是 4 个十六进制数字),因此这意味着我们必须处理结果的最高有效位。 0x2D130 中的“2”必须在某个地方,因为补码加法必须与我们添加的所有数字具有相同的长度。在一个补码加法中,我们将溢出的数字加回数字中。所以,0xD130+ 0x2 = 0xD132
然后标题字段的补码是:0xD132。
步骤 1b.2:
In octave:
-------------------
octave:14> hex2dec("4500")+hex2dec("062a")+hex2dec("42a1")+hex2dec("8001")+hex2dec("4210")+hex2dec("c0a8")+hex2dec("0001")+hex2dec("c0a8")+hex2dec("0003")
ans = 184624
octave:15> dec2hex(184624)
ans = 2D130
octave:16> 16^4
ans = 65536
octave:17> 184624-(2*65536)
ans = 53552
octave:18> 184624-(2*65536)+2
ans = 53554
octave:19> dec2hex(184624-(2*65536)+2)
ans = D132
octave:20>
步骤 1c.1:
将字段转换为二进制:
4500: 0100 0101 0000 0000
062A: 0000 0110 0010 1010
42A1: 0100 0010 1010 0001
8001: 1000 0000 0000 0001
4210: 0100 0010 0001 0000
C0A8: 1100 0000 1010 1000
0001: 0000 0000 0000 0001
C0A8: 1100 0000 1010 1000
0003: 0000 0000 0000 0011
4500+062A:
00 0100 0101 0000 0000+
00 0000 0110 0010 1010
-----------------------
00 0100 1011 0010 1010 (04B2A)
+42A1
00 0100 1011 0010 1010+
00 0100 0010 1010 0001
-----------------------
00 1000 1101 1100 1011 (08DCB)
+8001
00 1000 1101 1100 1011+
00 1000 0000 0000 0001
------------------------
01 0000 1101 1100 1100 (10DCC)
+4210
01 0000 1101 1100 1100+
00 0100 0010 0001 0000
------------------------
01 0100 1111 1101 1100 (14FDC)
+C0A8
01 0100 1111 1101 1100+
00 1100 0000 1010 1000
-----------------------
10 0001 0000 1000 0100 (21084)
+0001
10 0001 0000 1000 0100+
00 0000 0000 0000 0001
-----------------------
10 0001 0000 1000 0101 (21085)
+C0A8
10 0001 0000 1000 0101+
00 1100 0000 1010 1000
-----------------------
10 1101 0001 0010 1101 (2D12D)
+0003
10 1101 0001 0010 1101+
00 0000 0000 0000 0011
-----------------------
10 1101 0001 0011 0000 (2D130)
步骤 1c.2:
将数字加 10(最左边的位):
1101 0001 0011 0000+
0000 0000 0000 0010
--------------------
1101 0001 0011 0010 (D132)
第 2 步。
无论您如何进行反码加法,您现在都必须取结果的反码。任何二进制数的补码只是“翻转数字中的所有位”的花哨名称:
1101 0001 0011 0010 -> 0010 1110 1100 1101 (2ECD)
如果你想取一个十六进制数字的补码而不转换为二进制,下面是一个方便的表格:
| n |
1' |
| 0 |
F |
| 1 |
E |
| 2 |
D |
| 3 |
C |
| 4 |
B |
| 5 |
A |
| 6 |
9 |
| 7 |
8 |
| 8 |
7 |
| 9 |
6 |
| A |
5 |
| B |
4 |
| C |
3 |
| D |
2 |
| E |
1 |
| F |
0 |
所以取 D132 的补码为:
D->2
1->E
3->C
2->D
所以 IPv4 标头 4500 062A 42A1 8001 4210 XXXX C0A8 0001 C0A8 0003 的校验和是 0x2ECD。
一个演示步骤的小型围棋程序:
https://go.dev/play/p/DOj28mjuqtP