在Python中计算ICMP回显请求的校验和

时间:2019-03-18 10:10:56

标签: python-3.x checksum icmp

我正在尝试在Python中实现ping服务器,并且正在查看Pyping的源代码作为参考:https://github.com/Akhavi/pyping/blob/master/pyping/core.py

我无法理解为计算ICMP回显请求的校验和而实现的 calculate_checksum 功能。它的实现方式如下:

def calculate_checksum(source_string):

    countTo = (int(len(source_string) / 2)) * 2
    sum = 0
    count = 0

    # Handle bytes in pairs (decoding as short ints)
    loByte = 0
    hiByte = 0
    while count < countTo:
        if (sys.byteorder == "little"):
            loByte = source_string[count]
            hiByte = source_string[count + 1]
        else:
            loByte = source_string[count + 1]
            hiByte = source_string[count]
        sum = sum + (ord(hiByte) * 256 + ord(loByte))
        count += 2

    # Handle last byte if applicable (odd-number of bytes)
    # Endianness should be irrelevant in this case
    if countTo < len(source_string): # Check for odd length
        loByte = source_string[len(source_string) - 1]
        sum += ord(loByte)

    sum &= 0xffffffff # Truncate sum to 32 bits (a variance from ping.c, which
                      # uses signed ints, but overflow is unlikely in ping)

    sum = (sum >> 16) + (sum & 0xffff)  # Add high 16 bits to low 16 bits
    sum += (sum >> 16)                  # Add carry from above (if any)
    answer = ~sum & 0xffff              # Invert and truncate to 16 bits
    answer = socket.htons(answer)

    return answer

总和&= 0xffffffff 用于将总和截断为32位。但是,多余的位(第33位)发生了什么。难道不应该将其作为进位加到总和上吗?另外,在此之后,我将无法理解代码。

我阅读了RFC1071文档(http://www.faqs.org/rfcs/rfc1071.html),该文档解释了如何实现校验和,但是我对此了解不多。

任何帮助将不胜感激。谢谢!

1 个答案:

答案 0 :(得分:0)

我终于能够弄清 calculate_checksum 函数的工作原理,并且我尝试在下面进行解释。

校验和计算如下(根据RFC1071):

  • source_string中的相邻八位字节配对以形成16位 整数,而这些整数的1's complement sum为 计算。 如果八位位组的数目为奇数,则在n-1个八位位组中创建对,然后将其相加,然后将剩余的八位位组添加到总和中。

  • 结果和被截断为16位(要注意进位),并通过取1的补码来计算校验和。最终的校验和应为16位长。

让我们举个例子。

如果要对八位字节[A,B,C,D,E]序列计算校验和,则创建的对将为[A,B]和[C,D],其余八位字节为E。对[a,b]可以计算如下:

  

a * 256 + b ,其中a和b是八位位组

假设a为11001010,b为00010001,则a * 256 + b = 1100101000010001,从而得出八位位组的连接结果。

1的补码和计算如下:

  

sum = [A + B] +'[C + D] +'E,其中+'代表1的补码   加

现在回到代码,在 sum&= 0xffffffff 行之前的所有内容都会计算出我们之前计算出的1的补码和。


总和&= 0xffffffff

用于将总和截断为32位,尽管由于source_string的大小不是很大,所以ping中不大可能超过总和

(source_string =标头(8个字节)+有效载荷(可变长度))。


总和=(总和>> 16)+(总和&0xffff)

这段代码是针对总和大于16位的情况实现的。总和分为两部分:

(sum >> 16):高阶16位

(sum&0xffff):低阶16位

,然后添加这两个部分。最终结果可能是比16位多16位


总和+ =(总和>> 16)

此行用于上一次计算所得的总和长于16位的情况,并用于处理进位,类似于上一行。


最后,将计算1的补码并将其截断为16位。 socket.htons()函数用于根据设备的体系结构(小字节序和大字节序)维护发送到网络的字节排列。