背景
公司某个服务发送TCP报文后,得到的响应是非预期数据
原因竟然是:TCP包的 payload 数据某个bit位被翻转,但是 checksum 的值一样,错误的包被分发给了上层服务
Checksum介绍
IP 头有自己的 Checksum,TCP、UDP 也有自己的 Checksum,分别校验不同部分的数据
IP 头的 Checksum 用于校验 IP 头的数据是否正确
TCP 的 Checksum 用于校验 TCP 头、TCP 数据部分是否正确
UDP 的 Checksum 用于校验 UDP 头、UDP 数据是否正确
TCP的校验和(Checksum)是用于检测 TCP 数据包中的数据错误的重要机制
它在数据传输过程中强制执行了数据完整性检查,以确保数据在传输过程中没有被损坏
TCP 校验和是 TCP 头的一部分,它覆盖了 TCP 头和 TCP 数据,还涉及到 IP 头的部分内容
校验和计算原理
TCP校验和是通过一种特定的方法计算出来的,它包括以下几部分内容:
- 伪头部(Pseudo-Header)
- TCP头
- TCP数据
伪头部
伪头部并不是实际传输的数据的一部分,而是一种在计算校验和时,用来增强数据完整性的机制
伪头部包含了一些 IP 层的信息
伪头部总共 12 字节(源IP、目的IP、占位符、协议、TCP长度)
伪头部格式如下
±---------------------±---------------------±---------------------±---------------------+
| 源地址 (32位) | 目的地址 (32位) |
±---------------------±---------------------±---------------------±---------------------+
| 零 (8位) | 协议 (8位) | TCP长度 (16位) |
±---------------------±---------------------+
- 源地址:发送方的IP地址
- 目的地址:接收方的IP地址
- 零:8位的0值
- 协议:8位的值,6代表TCP协议
- TCP长度:TCP头和TCP数据部分的总长度,以字节为单位
计算校验和
校验和是通过计算16位字的和,然后取其反码(二进制反码)得到
具体步骤如下:
1、 先将需要计算checksum数据中的checksum设为0
2、 计算checksum的数据按2byte划分开来,每2byte组成一个16bit的值,如果最后有单个byte的数据,补一个byte的0组成2byte
3、 将所有的16bit值累加到一个32bit的值中
4、 将32bit值的高16bit与低16bit相加到一个新的32bit值中,若新的32bit值大于0Xffff,再将新值的高16bit与低16bit相加
5、 将上一步计算所得的16bit值按位取反,即得到checksum值,存入数据的checksum字段即可
校验和字段
TCP头中的第17~18字节(从1开始)是校验和字段
发送方在计算校验和时,这两个字节初始化为0 (注意,初始化为 0xFFFF 也出现过问题)
计算所得的校验和填充到这两个字节,接收方则通过相同的方式重新计算校验和,并与接收到的校验和进行比较以验证数据完整性
检验和,2B,检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。
示例代码
static u16_t chksum(void *dataptr, u16_t len)
{
u32_t acc;
u16_t src;
u8_t *octetptr;
acc = 0;
octetptr = (u8_t*)dataptr;
while (len > 1) {
src = (*octetptr) << 8;
octetptr++;
src |= (*octetptr);
octetptr++;
acc += src;
len -= 2;
}
if (len > 0) {
src = (*octetptr) << 8;
acc += src;