为什么 ICMP 校验和要移动 16 位

why is ICMP checksum shifted 16 bits

我很难理解为什么 ICMP 校验和总数(在被补充之前)是 this line of code 中的总数 + 右移 16 位的总数:

checksum bs = let bs' = (if (BL.length bs) `mod` 2 == 0 then bs else BL.snoc bs 0)
                  ws = runGet listOfWord16 bs'
                  total = sum (map fromIntegral ws) :: Word32
              in complement (fromIntegral total + fromIntegral (total `shiftR` 16))

RFC 792 关于计算校验和有这样的说法:

Checksum

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.

明白为什么要计算bs',按"If the total length is odd, the received data is padded with one octet of zeros for computing the checksum."

的要求

我也能理解将这行代码中完成的16位字的总和total = sum (map fromIntegral ws) :: Word32

我只是想不通为什么会出现在这行代码中:

complement (fromIntegral total + fromIntegral (total `shiftR` 16))

+ fromIntegral (total `shiftR` 16) 应该包括在内。

注意:我已经用 wireshark 验证了校验和只有在我补充 total + total `shiftR` 16 时才正确,就像在链接的代码行中所做的那样。所以我知道它是正确的,我只是不明白为什么。

RFC 1071详细描述了校验和的定义,包括重要的部分:

On a 2's complement machine, the 1's complement sum must be computed by means of an "end around carry", i.e., any overflows from the most significant bits are added into the least significant bits.

在您的代码中,

total = sum (map fromIntegral ws) :: Word32

是32位和,即它的低16位是不带进位的和,高16位是带进位的和。通过使用 fromIntegral :: Word32 -> Word16 确实截断的事实,我们有

low = fromIntegral total :: Word16
high = fromIntegral $ total `shiftR` 16 :: Word16

所以我们可以将 "end around carry" 计算为

eac = low + high