为什么ICMP校验和移位了16位

时间:2017-06-29 01:48:07

标签: haskell checksum icmp

我很难理解为什么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 有关于计算校验和的说法:

  

校验

     

校验和是16位的校验和的补码     补充以ICMP类型开头的ICMP消息的总和。     为了计算校验和,校验和字段应为零。     如果总长度是奇数,则用一个填充接收的数据     用于计算校验和的零八位字节。这个校验和可能是     将来取代。

我理解为什么计算bs',如所要求的那样“如果总长度是奇数,则接收的数据用一个零的零填充来计算校验和。”

我也可以理解在代码的这一行total = sum (map fromIntegral ws) :: Word32

中总结16位字的总和

我只是无法弄清楚为什么在这行代码中:

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

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

注意:我已经通过wireshark验证校验和是否正确,如果我补充total + total `shiftR` 16,就像在链接的代码行中一样。所以我知道这是正确的,我只是不明白为什么。

1 个答案:

答案 0 :(得分:4)

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

  

在2的补码机器上,1的补码总和必须是   通过“结束携带”计算,即任何溢出   从最重要的位添加到最少   重要的一点。

在您的代码中,

total = sum (map fromIntegral ws) :: Word32

是32位和,即它的低16位是没有进位的和,高16位将包含进位的总和。通过使用fromIntegral :: Word32 -> Word16截断的事实,我们有

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

所以我们可以计算“结束携带”作为

eac = low + high