翻译惹的祸: IP、ICMP、TCP和UDP包的校验和的那些事

一、网络协议校验和定义

RFC 1071 中说到校验和的定义:

(1) Adjacent octets to be checksummed are paired to form 16-bit integers, and the 1's complement sum of these 16-bit integers is formed.

(2) To generate a checksum, the checksum field itself is cleared, the 16-bit 1's complement sum is computed over the octets concerned, and the 1's complement of this sum is placed in the checksum field.

(3) To check a checksum, the 1's complement sum is computed over the same set of octets, including the checksum field. If the result is all 1 bits (-0 in 1's complement arithmetic), the check succeeds.

IP校验和是报头中所有16位字的补码之和的16个位的补码。

许多人可能会问的一个问题是“1的补码和是多少?”。这是因为所有计算机都使用‘2的补码’表示,而不使用‘1的补码’。以下简要介绍。

那么什么是 1的补码(ones's complement sum)、什么是2的补码(two's complement sum):

1.1什么是1的补码(反码、ones's complement sum、1's complement sum)

1的补码(ones's complement sum): 也称为 反码 。之所以称为“反码”,是因为反码表示的负数,在形式上可以看做对其所对应正数(其相反数)的“按位求反”(将所有0换为1,1换为0),例如,十进制数6,用8位二进制数表示为00000110,则-6表示为11111001。

这一名称十分直观地体现了反码的外貌,通俗易懂。但是“1的补码”这个名字就有点让人摸不着头脑了,完全不明白是什么意思。其实,这是翻译的锅。 反码的英文叫做 Ones' complement (注意不是 One's complement),意即“(复数个)一的补”。由于汉语中名词不能通过词形变化来体现单复数,我们无法通过译名来体会这个名字的含义。仍取上面的例子,我们将反码的+6与-6按二进制加法相加,发现得到了“一串一”。

仔细想想可以发现,任意一对相反数的反码的“和”都是一串一,所以我们可以这样定义负数N的n位二进制反码:

这便是反码叫做“(复数个)一的补”的原因。

1.2什么是2的补码(反码、two's complement sum、2's complement sum)

然后你是不是以为“二的补”就是这样:

并不!二进制数中不会出现“2”。事实上,“2的补码”,英文是 Two's complement (而不是Twos' complement),所以它是“(一个)2的补码”,与“1的补码”其实不是同一个意思。负数的补码的定义如下:

这里的“two”就是指上式中的“2”。

总结:

“1的补码”是指“以一串一为模求补”
  “2的补码”是指“以二的n次幂为模求补”

1.3举个栗子

2的补码定点整数(8-bit)

Binary Decimal Hex
00000000 0 00
00000001 1 01
00000010 2 02
00000011 3 03
11111111 -1 FF
11111110 -2 FE
11111101 -3 FD

两个整数相加:

-3 + 5 = 2

FD + 05 = 01 02

丢弃进位(01)会得到正确的结果。

1的补码定点整数(8-bit)

Binary Decimal Hex
00000000 0 00
00000001 1 01
00000010 2 02
00000011 3 03
11111111 -0 FF
11111110 -1 FE
11111101 -2 FD
11111100 -3 FC

同样的两个数相加:

-3 + 5 = 2

FC + 05 = 01 01

将进位(01)相加到低位(01)会得到正确的结果:

01 + 01 = 02

因此,1的补码和是通过对数字求和并在结果中加上一个或多个进位来完成的。

一个简单的例子

假设我们有一个使用2的补码的8位机器并且发送一个数据包:

FE 05 00

00是校验和字段。

让我们计算和校验网络校验和,这个普通的相加得到的结果:

FE + 05  =  01 03

1的补码和要求将进位(01)加到结果里:

03 + 01 = 04 

所以FE + 05的1的补码和是04。

1的补码的1的补码和就是:

~04  = FB

数据包将会是:

FE 05 FB 

现在,在接收端我们将收到的字节相加,包括校验和:

FE + 05 + FB  = 01 FE 

这1的补码和是:

FE + 01 = FF = -0 

校验和为-0说明是ok的。

另一个复杂一点的例子(32位机器):

伪数据包:

01 00 F2 03 F4 F5 F6 F7 00 00

(00 00 是校验和字段)

按16位分组:

0100 F203 F4F5 F6F7

计算求和:

0100 + F203 + F4F5 + F6F7 = 0002 DEEF (值存在32位内存)

将进位(0002)加到结果里,得到1的补码和:

DEEF + 002 = DEF1

计算1的补码和的1的补码(Calculate 1's complement of the 1's complement sum):

~DEF1 = 210E

包含校验和(21 0E)的数据包:

01 00 F2 03 F4 F5 F6 F7 21 0E

在接收端:

0100 + F203 + F4F5 + F6F7 + 210E = 0002 FFFD

FFFD + 0002 = FFFF

校验结果为FFFF,说明校验通过。

1.4总结

在2的补码机上使用1的补码加法可能看起来很笼统。但是,这种方法有其自身的优点。

可能最重要的是它是字节序独立的。小端字节序计算机将LSB(Least Significant Bit--最低有效位)放在最后(例如Intel处理器)。 大端字节序计算机将LSB放在首位(例如IBM大型机)。当将进位加到LSB上以形成1的补码和时(请参见示例),我们加03 + 01还是01 + 03都没关系。结果是相同的。

1、LSB(Least Significant Bit)--最低有效位

LSB代表二进制中最小的单位,可以用来指示数字很小的变化。也就是说,LSB是一个二进制数字中的第0位(即最低位),具有权值为2^0,可以用来检测数的奇偶性。

2、MSB(Most Significant Bit)--最高有效位

MSB代表一个n位二进制数字中的n-1位,具有最高的权值2^(n-1).对于有符号的二进制数,负数采用反码或补码形式,此时MSB用来表示符号,msb为1表示负数,0表示正数。

其他好处包括易于检查传输和校验和计算,以及通过仅更新已更改的IP协议的字段来加快计算速度的多种方法。

二、ICMP协议与校验和

2.1 ICMP协议

wiki对ICMP协议的介绍

三、ICMP实现[rust版本]

未完待续

参考:

1、 《Short description of the Internet checksum》

2、 《反码,补码为什么又叫“一的补”,“二的补”?》

3、 RFC 1071

4、

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章