【完整代码】详解ICMP协议校验和的计算方式,清晰易懂

别摸鱼啦,说的就是你,学习编程从入门到放弃。掌握编程思维方式,远胜于死记硬背编程语法,不能再迷路了。


ICMP协议报文结构(标准)

ICMP协议常用的回送与回送响应结构(ping)

图一 ICMP协议报文结构


ICMP协议校验和的计算方式

对于ICMP协议校验和的计算方式,参考RFC官方文档给出的说明:

The checksum is the 16-bit ones's complement of the one's
complement sum of the ICMP message starting with the ICMP Type.
For computing the checksum , the checksum field should be zero.
If the total length is odd, the received data is padded with one
octet of zeros for computing the checksum.  This checksum may be
replaced in the future.

总体的思路和步骤如下:

一、获取ICMP报文(首部+数据部分)

二、将ICMP报文中的校验和字段置为0。

三、将ICMP协议报文中的每两个字节(16位,需要注意大小端问题)两两相加,得到一个累加和。若报文长度为奇数,则最后一个字节(8-bit)作为高8位,再用0填充一个字节(低8-bit)扩展到16-bit,之后再和前面的累加和继续相加得到一个新的累加和。

四、(若有溢出)将累加和的高16位和低16位相加,直到最后只剩下16位。

五、将最后得到的16位结果取反(按位取反)作为校验和的值。


ICMP协议校验和计算示例

以ICMP协议的ping回送响应报文(大端)为例:

0x08 0x00 0xeb 0xc9 0x00 0x0e 0x00  0x1f 0x00 0x01 0x02 0x03 0x04 0x05 0x06 

1. 先将ICMP协议的校验和字段置为0

0x08 0x00 0x00 0x00 0x00 0x0e 0x00 0x1f 0x00 0x01 0x02 0x03 0x04 0x05 0x06 

2. 将ICMP协议报文中的每两个字节(16位)(需要注意大小端问题,网络字节序是大端模式,如0xebc9, 对于小端主机来说,组成的是0xc9eb,应转化为主机字节序:0xebc9)两两相加,得到一个累加和。若报文长度为奇数,则最后一个字节(8-bit)作为高8位,再用0填充一个字节(低8-bit)扩展到16-bit,之后再和前面的累加和继续相加得到一个新的累加和。

0x0800 + 0x0000 + 0x000e + 0x001f + 0x0001+ 0x0203 + 0x0405 + 0x0600 = 0x1436

3. 将累加和的高16位和低16位相加,直到最后只剩下16位

0x0000 + 0x1436 = 0x1436

4. 将最后得到的16位结果取反(按位取反)作为校验和字段的值

0001 0100 0011 0110 (0x1436 取反)
1110 1011 1100 1001 (0xebc9)


[C/C++] ICMP协议校验和计算代码示例

/**                                                            
 * icmp_calc_checksum:
 * @size: the icmp data packet length
 * @icmp_data: icmp protocol packet both header and data
 *
 * Calc icmp's checksum:
 * if we divide the ICMP data packet is 16 bit words and sum each of them up
 * then hihg 16bit add low 16bit to sum get a value,  
 * If the total length is odd, 
 * the last byte is padded with one octet of zeros for computing the checksum.
 * Then hihg 16bit add low 16bit to sum get a value,
 * finally do a one's complementing 
 * then the value generated out of this operation would be the checksum.
 * 
 * Return: unsigned short checksum
 */
unsigned short icmp_calc_checksum(char * icmp_packet, int size)
{
 unsigned short * sum = (unsigned short *)icmp_packet;
 unsigned int checksum = 0;
 while (size > 1) {
  checksum += ntohs(*sum++);
  size -= sizeof(unsigned short);
 }
 if (size) {
  *sum = *((unsigned char*)sum);
  checksum += ((*sum << 8) & 0xFF00);
 }

 checksum = (checksum >> 16) + (checksum & 0xffff);
 checksum += checksum >> 16;
 
 return (unsigned short)(~checksum);
}

总结

所有文章中给出的代码和示例,都是通过测试验证后贡献给大家的,文章中有些地方可能会出现笔误,欢迎童鞋们及时指出并加以更正。

关注小编,每篇文章都有不一样的体验和感悟,可以留言或者私信小编沟通交流,一起学习呦。


创作不易,动动发财的小手点个免费的关注再走呗!

  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值