关于TCP校验和的一些实验体会

一、校验算法介绍

对于IP数据包来说,校验和用于检验首部的有效性和完整性,首部发生变化时。如减少TTL、减少或改变选项、发生分片等,都要重新计算校验和。过程是先把首部检验和字段置为0,然后把首部分成一块16bit的数据来处理,计算每个16bit的整数的反码,再求他们的和,最后计算出结果的反码。方法二可以直接求每个16bit的和,最后在把高16bit的数据加到低16bit位中(最后高16bit是全为0的),在求反码。

例如求和之后结果为0x7ffff,高位加到低位 0x7+0xffff = 0x10006 ,高位还有数据,继续高位加到低位 0x6 + 0x1 = 0x7,之后求反码 0xffff - 0x7 = fff8。fff8就为校验和。

TCP校验和算法是用TCP伪首部+TCP首部+数据的16bit之和来计算,而且里面的字段1个字节以上的必须要要转换成网络字节序。假如数据是奇数位的话,那最后8bit的数据就要用0填充为16bit的数据,如0x12 0x34 0x56,则变成0x12+0x34+0x5600。

校验和就简单的说明一下,算法网上随便都可以搜到,我想要说的重点不是这个。

这里写图片描述

二、校验和与字节序的问题

在刚开始了解校验和的时候,觉得网上提供的资料虽然已经到位,但是不够全面,因为总觉得有一些繁杂的东西没有梳理清楚。这一节要说的就是**字节序**的问题。学习网络编程,字节序就是其中的一个麻烦的绊脚石,即使你前一段时间想通了它,但是不过多久就会忘了。

问1:校验和计算为什么都用主机字节序?
主机在校验过程中就按主机字节序来读取数据,因为校验的过程是对称的,与大小尾端无关。
例如有以下数据0x12,0x34
校验和为EDCB,如果我反过来读取数据,结果也是一样的。0x3412+CBED = FFFF。

问2:检验和为什么不需要字节序转换?
试想一下,数据包流向一个环回地址,则主机接收自己发出的数据包,则数据包的校验和同样由本机处理。计算的数据如果进行了字节序转换,那和原来校验和取反之前的结果相加就不为FFFF了。
例如有以下数据0x12,0x34,0x56,0x78
校验和 = ~(0x12 + 0x34 + 0x56 + 0x78)= 0x9753
就有 0x9753 + 0x12 + 0x34 + 0x56 + 0x78 = FFFF
如果把校验和字节序转换,那上述结构就不会等于FFFF了。

三、校验和代码的缺陷

先给出C的代码。

USHORT checksum(USHORT* buffer, int size)
{
    unsigned long cksum = 0;

    while(size>1)
    {
        cksum += *buffer++;
        size -= sizeof(USHORT);
    }
    if(size)
    {
        cksum += *(UCHAR*)buffer;           //问题所在处!!!!!
    }
    cksum = (cksum>>16) + (cksum & 0xffff); //将高16bit与低16bit相加
    cksum += (cksum>>16);                   //将进位到高位的16bit与低16bit 再相加

    return (USHORT)(~cksum);
}

问题就出在如果数据是奇数位的话,读取数据会不会出错呢,答案是会出错的。

因为之前在了解校验和算法的时候,一直记得奇数位最后的8bit数据一定要填充为16bit的数据来计算,所以看到代码在处理奇数位数据的时候用的是UCHAR型,意思为取一个字节的数据加到和的最低字节。
后来试着从最后一位数据处理的代码片里面用UCHAR和USHORT类型输出数据,发现结果是一样的。观察了很久才知道主机是小尾端字节序的。就不禁引出疑问,如果是大尾端的机器,那会不会结果不一样呢?

有以下数据0x12,0x34,0x56,0x00
小尾端读取到的数据是 0x3412, 0x56
理论大尾端读取到的数据是0x1234, 0x5600,实际上是0x1234, 0x00
所以这个算法要修改 cksum += (UCHAR)buffer 为 cksum += (USHORT)buffer。

但是问题还没解决,依然存在着缓冲区对数据的影响。
如果上述数据0x56的后面不是0x00的话,而是其他不属于计算范围的数据,就会导致取到错误的数据。所以缓冲区一定要大于数据长度,并且还要初始化。

同理IP首部的长度32bit为单位的,计算IP首部不会存在奇数位。

以上是本人的一些看法,若有不足之处,希望各位大牛多加指点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值