IP/UDP/TCP/ICMP数据报协议的校验和的区别和计算
1、现针对各种协议数据包校验的区别总结如下:
(1)IP校验和:
IP数据报的校验和只检验IP数据报的首部。(2)UDP校验和:
UDP数据报计算校验和的方法和IP数据报校验和的方法相似,但是UDP的校验和是将首部和数据部分一起都校验。并且在计算UDP校验和之前需要封装一个伪首部,伪首部结构如下(具体结构定义见后面代码部分):
源IP地址 | 目的IP地址 | 全 0 | 协 议 | UDP长度
(3)TCP校验和:
TCP 的校验和计算方法同UDP一样,同样要加上一个伪头部,区别是伪头部的协议码是0x06,长度是整个TCP报文的长度(包含TCP头部)。(4)ICMP校验和:
ICMP校验和的计算方法一样,只不过只是对ICMP包整个进行校验和,没有伪头部,也不包括IP包头部。总结如下: 计算IP数据报和ICMP的校验和时不需要封装伪首部,他俩不同的是IP数据报只检验IP数据报的首部,而ICMP数据报校验包括ICMP头部和数据部分(整个ICMP长度),TCP和UDP的校验方法和IP、ICMP校验方法一样,但是TCP和UDP校验之前需要封装伪首部。还有一个需要注意的是校验的顺序是从上(层)到下(层),如校验ICMP时,先校验ICMP后校验IP(ICMP数据包结构=ether + IP + ICMP), 并且校验之前需要先将校验和的值初始化为0.
2、以上的几种数据报的校验和的计算方法都一样,把校验部分看成以16位为单位的数字组成,依次进行二进制反码求和。
具体如下:unsigned short check_sum(unsigned short *packet,int packlen)
{
register unsigned long sum = 0;
while (packlen > 1)
{
sum += *(packet++);
packlen -= 2;
}
if (packlen > 0)
sum += *(unsigned char *) packet;
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (unsigned short) (~sum);
}
3、应用举例(UDP校验):
注:当一个数据包中有多个数据报协议时,校验的顺序必须是从上往下,例:一个数据报的封装为 以太网首部+IP首部+UDP首部+数据,那么在计算校验和的步骤如下:
(1)分别将IP和UDP的校验和置0;
(2)计算UDP校验和(包括已封装伪首部的UDP头+数据);
(3)计算IP校验和;
下面为计算UDP校验和的示例:
//定义校验UDP时的伪首部结构体
typedef struct {
struct in_addr s_addr; //源IP
struct in_addr d_addr; //目的IP
unsigned char mbz; //全0
unsigned char ptcl; //协议
unsigned short plen; //UDP长度(UDP头+数据)
}udp_psd_header;
udph->check = 0; //不能省略
udph->check = udp_chksum(iph, udp_len);
//校验和,这里传入参数iph(IP首部指针)是因为封装UDP伪首部时需要知道源IP、目的IP,而知道了IP首部指针,自然也就知道了UDP首部指针
iph->ip_sum = 0; //不能省略
iph->ip_sum = check_sum((unsigned short*)iph, sizeof(struct ip)); //校验和
//UDP校验和程序
//校验UDP/TCP首部首先需要封装一个为首部,然后再校验,校验长度包括头和数据部分
unsigned short udp_chksum(void *data_ip,int udp_len) //udp_len = UDP首部+数据
{
udp_psd_header * pudp_psd_header;
pudp_psd_header = (udp_psd_header *)malloc(udp_len+sizeof(udp_psd_header));
if(!pudp_psd_header)
return -1;
memset(pudp_psd_header,0,udp_len+sizeof(udp_psd_header));
//封装伪首部
pudp_psd_header->s_addr= ((struct ip *)data_ip)->ip_src;
pudp_psd_header->d_addr= ((struct ip *)data_ip)->ip_dst;
pudp_psd_header->mbz=0;
pudp_psd_header->ptcl=IPPROTO_UDP;
pudp_psd_header->plen=htons(udp_len);
memcpy((unsigned char *)pudp_psd_header+sizeof(udp_psd_header),(unsigned char *)data_ip+sizeof(struct ip),udp_len);
return check_sum((unsigned short *)pudp_psd_header,udp_len+sizeof(udp_psd_header));
}