校验和算法
校验和的计算方法在文档 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.
即首先将校验和字段清零,将待求和数据调整为偶数字节(如为奇数字节则最后一个字节扩展为字)。然后用反码相加法(进位加到低位上)、以字为单位累加待求和数据。最后将累加结果取反并截低 16 位作为校验和。
之所以使用反码相加法,是为了让计算结果和网络序或主机序无关。
根据这个规则,计算校验和的的 C 语言函数可以做如下实现。
1 | uint16_t |
下面会使用这个校验和计算函数分别计算 IP、ICMP、TCP 和 UDP 包的校验和。
IP 包校验和的计算
IP 包校验和的计算范围在 RFC 791 中有如下说明:
The checksum field is the 16 bit one’s complement of the one’s
complement sum of all 16 bit words in the header. For purposes of
computing the checksum, the value of the checksum field is zero.
即 IP 包的校验和只计算包头。
根据描述,IP 包的校验和可用 C 语言做如下计算。
1 | struct iphdr *ipheader; |
ICMP 包校验和的计算
ICMP 包校验和的计算范围在 RFC 792 中有如下说明:
The checksum is the 16-bit ones’s complement of the one’s
complement sum of the ICMP message starting with the ICMP Type.
For computing the checksum , the checksum field should be zero.
This checksum may be replaced in the future.
即 ICMP 包的计算范围包括包头和数据。
根据描述,假设 IP 包校验和已经计算完毕,那么其中的 ICMP 包校验和可以用 C 语言做如下计算。
1 | struct icmphdr *icmpheader; |
TCP 和 UDP 包校验和的计算
伪首部
TCP 和 UDP 校验和的计算要稍微麻烦一些,因为需要引入一个伪首部(pseudo header),伪首部的结构在 RFC 768 中有如下说明:
The pseudo header conceptually prefixed to the UDP header contains the
source address, the destination address, the protocol, and the UDP
length. This information gives protection against misrouted datagrams.
This checksum procedure is the same as is used in TCP.0 7 8 15 16 23 24 31 +--------+--------+--------+--------+ | source address | +--------+--------+--------+--------+ | destination address | +--------+--------+--------+--------+ | zero |protocol| UDP length | +--------+--------+--------+--------+
可见 TCP 和 UDP 的伪首部结构完全一致。
根据描述,伪首部的结构可以用 C 语言结构体做如下实现。
1 | typedef struct pseudohdr |
TCP 包校验和的计算
TCP 包校验和的计算方法在 RFC 793 中有如下说明:
The checksum field is the 16 bit one’s complement of the one’s
complement sum of all 16 bit words in the header and text. If a
segment contains an odd number of header and text octets to be
checksummed, the last octet is padded on the right with zeros to
form a 16 bit word for checksum purposes. The pad is not
transmitted as part of the segment. While computing the checksum,
the checksum field itself is replaced with zeros.The checksum also covers a 96 bit pseudo header conceptually
可见,算法和之前提到的校验和算法完全一致,根据描述校验和的计算需要包含伪首部和整个 TCP 包。
根据描述,假设 IP 包校验和已经计算完毕,那么其中的 TCP 包校验和可以用 C 语言做如下计算。
1 | char *tcpsumblock; /* 伪首部 + TCP 头 + 数据 */ |
UDP 包校验和的计算
UDP 包校验和的计算方法在 RFC 768 中有如下说明:
Checksum is the 16-bit one’s complement of the one’s complement sum of a
pseudo header of information from the IP header, the UDP header, and the
data, padded with zero octets at the end (if necessary) to make a
multiple of two octets.The pseudo header conceptually prefixed to the UDP header contains the
source address, the destination address, the protocol, and the UDP
length. This information gives protection against misrouted datagrams.
This checksum procedure is the same as is used in TCP.
所以 UDP 包校验和的计算方法和 TCP 包如出一辙,同样包含了一个伪首部。
具体的实现可以参考之前计算 TCP 包校验的 C 语言实现。