前几篇已经讲解了TCP保证可靠性中的,序列号(按序到达)和确认应答机制,为了讲解TCP校验和又把IP包的校验和讲了,今天这篇博文就该讲讲TCP的校验和了,后续也通过实例抓包进行实际验证。
关于TCP头部结构及字段说明,在之前的华为网络基础 | TCP头部封装篇章已将将结果了,这儿不再过多赘述,具体的知识点请点击超链接查看学习。
这次的重点将是TCP校验和(Checksum)的原理和抓包实际验证
一、TCP校验和概述
TCP校验和(Checksum)是一个端到端的校验和,由发送端计算,然后由接收端验证。其目的是为了发现TCP首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到校验和有差错,则TCP段会被直接丢弃。
TCP校验和覆盖TCP首部和TCP数据,而IP首部中的校验和只覆盖IP的首部,不覆盖IP数据报中的任何数据。TCP校验和、IP校验和的计算方法是基本一致的,除了计算的范围不同。
TCP的校验和是必需的,而UDP的校验和是可选的。TCP和UDP计算校验和时,都要加上一个12字节的伪首部。
二、伪首部的概念
伪首部的数据都是从IP数据报头获取的。其目的是让TCP检查数据是否已经正确到达目的地,只是单纯为了做校验用的,如下图所示。
伪首部结构体如下:
struct
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz; //强制置空
char ptcl; //协议类型
unsigned short tcpl; //TCP长度
}psd_header;
伪首部共有12字节(前96Bits),包含如下信息:源IP地址、目的IP地址、保留字节(置0)、传输层协议号(TCP是6)、TCP报文长度(报头+数据)。
伪首部是为了增加TCP校验和的检错能力,如检查TCP报文是否收错了(目的IP地址)、传输层协议是否选对了(传输层协议号)等。
三、TCP校验和定义
先来看看RFC793对TCP校验和的定义:
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.
好吧,还是翻译成汉语……
1、把伪首部、TCP报头、TCP数据分为16位的字段,如果总长度为奇数个字节,则在最后增添一个位都为0的字节(切记切记!!)。把TCP报头中的校验和字段置为0(否则就陷入鸡生蛋还是蛋生鸡的问题);
2、用反码相加法累加所有的16位字(进位也要累加);
3、对计算结果取反,作为TCP的校验和。
四、算法
下面为C实现较为原始的checksum算法,代码中对于算法做了比较详细的注释:
unsigned short checksum(unsigned short * addr, int count)
{
long sum = 0;
/*计算所有数据的16bit对之和*/
while( count > 1 ) {
/* This is the inner loop */
sum += *(unsigned short*)addr++;
count -= 2
}
/*如果数据长度为奇数,在该字节之后补一个字节(0),然后将其转换为16bit整数,加到上面计算的校验和中。*/
if( count > 0 ) {
char left_over[2] = {0};
left_over[0] = *addr;
sum += * (unsigned short*) left_over;
}
/*将32bit数据压缩成16bit数据,即将进位加大校验和 的低字节上,直到没有进位为止。*/
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
/*返回校验和的反码*/
return ~sum;
}
哎呀!!算法又见算法,不好理解!好吧,下面还是通过实际抓包进行验证。还是那句话,认真看、做笔记!!
五、实例验证
从下图的WireShark抓包分析可以得出伪头部和TCP报文的一些信息[hide]
把以上所抓取的信息划到表格中如下:
再把表格里的信息转换成二进制如下:
按照如下过程进行:
1、把校验和字段置为0;
2、对TCP伪首部、TCP头部、数据中的每16bit进行二进制求和;
3、如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
4、将该16bit的值取反,存入校验和字段。
◆当接收TCP包时,需要对报头进行确认,检查TCP头是否有误,算法同上2、3步,然后判断取反的结果是否为0,是则正确,否则有错。
如下是计算步骤:
上图有一点要注意:数据只有1个字节,数据构成就为奇数字节了,所以还需要在后面加1字节的全0,保证是偶数字节。[/hide]
辛辛苦苦大半天,最终终于把校验和算出来了,AF78≠84c6 !!!!!!!!
纳尼,纳尼,纳尼,纳尼,纳尼,纳尼,纳尼,纳尼!!!!开始怀疑人生,我们是按照计算过程一步步做的,中间没有数据计算错误的,之前计算IP包的校验和却是可以的,现在计算TCP校验和居然是错的!!
小伙别慌,且等下篇博文分解!!!