发送icmp包时,需要计算icmp包的校验和,与ip包不同的是,icmp包需要连通头部信息加数据本身一起进行校验(ip包只需要校验头部信息)

校验方法如下:

  1. 把校验和字段置为0
  2. 将icmp包(包括header和data)以16bit(2个字节)为一组,并将所有组相加(二进制求和)
  3. 若高16bit不为0,则将高16bit与低16bit反复相加,直到高16bit的值为0,从而获得一个只有16bit长度的值
  4. 将此16bit值进行按位求反操作,将所得值替换到校验和字段

注意: 第四步中的按位求反值C语言的的按位求反(即~操作符),不是python中的按位求反

例如下图中的icmp包:

icmp抓包截图

其中icmp包的内容为:

08 00 F9 CE 7C E2 01 00 20 28 20 52 64 77 6A 6D 58 67 65 42 5A 2A 61 64 52 41 44 78 75 40 35 28 6F 76 20 43 56 76 59 20 4E 4F 64 20 20 6B 75 64 40 72 31 4A 50 20 20 33 67 20 2B 31 2A 51 20 52 70

校验字段为F9 CE

将校验字段置为0,然后以16bit为一组将全部字段二进制相加,即:

0800 + 0000 + 7CE2 + 0100 + 2028 + 2052 + 6477 + 6A6D + 5867 + 6542 + 5A2A + 6164 + 5241 + 4478 + 7540 + 3528 + 6F76 + 2043 + 5676 + 5920 + 4E4F + 6420 + 206B + 7564 + 4072 + 314A + 5020 + 2033 + 6720 + 2B31 + 2A51 + 2052 + 70 = 930fd

注意该数据的长度并不是16bit的整数倍,计算方式一样,只不过将最后8bit直接与之前相加

然后将结果的高16bit和低16bit继续相加:

0009 + 30fd = 3106

然后对结果(3106)进行c语言的按位求反操作(C语言的按位求反):

~3106 => CEF9

C语言的按位求反是让操作数的二进制位上的1变为0,0变为1, 而python的按位求反这是将操作数加一然后乘以负一(~5 == -6),两者之所以出现不同就在于操作数是否是unsigned**

附上最后的程序(注意一下如何将python的按位求反转为C语言的按位求反

def checksum(data):
    length = len(data)
    s = 0
    n = length % 2
    # 分割数据每两比特(16bit)为一组
    for i in range(0, length - n, 2):
        s += ord(data[i]) + (ord(data[i+1]) << 8)
    # 如果数据长度为基数,则将最后一位单独相加
    if n:
        s += ord(data[-1])
    # 重复将高16位与低16位相加直到高16位为0
    while (s >> 16):
        s = (s & 0xFFFF) + (s >> 16)
    s = ~s & 0xffff
    # 返回的是十进制整数
    return s