ICMP,IP,UDP,TCP报头部分都有checksum(检验和)字段。ICMP和IP报头校验和的计算都很简单,过程如下:
1.把校验和字段置为0;
2.对IP头部中的每16bit进行二进制求和;
3.如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
4.将该16bit的值取反,存入校验和字段。
//计算校验和
USHORT checksum(USHORT *buffer,int size)
{
unsigned long cksum=0;
while(size>1)
{
cksum+=*buffer++;
size-=sizeof(USHORT);
}
if(size)
{
cksum+=*(UCHAR *)buffer;
}
//将32位数转换成16
while (cksum>>16)
cksum=(cksum>>16)+(cksum & 0xffff);
return (USHORT) (~cksum);
}
UDP/TCP报头中的校验和的计算比较复杂的,要用到 UDP/TCP伪首部:先要填充伪首部各个字段,然后再将UDP/TCP报头以后(包括报头)的数据附加到伪首部的后面,再对位首部使用上述校验和计算,所得到的值才是UDP/TCP报头部分的校验和。 位首部可以用如下的结构体表示:typedef struct{
ULONG sourceip; //源IP地址
ULONG destip; //目的IP地址
BYTE mbz; //置空(0)
BYTE ptcl; //协议类型
USHORT plen; //TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)
}Psd_Header; 注:1.对于UDP协议,如果不想计算的话,可以把校验和的两个字节置0,根据RFC的规定,这样上层UDP协议就不会计算校验和了2.sniffer查看校验和时,最好在中间节点。你在本机抓的时候,你的协议驱动在NDIS架构中层次太高,与标准协议栈是平级的,此时校验和处未被计算填充,还是垃圾填充数据。你在网络其它节点抓的时候,报文已经经过网卡的处理,校验和被正确设置了。