我很难理解为什么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
我只是无法弄清楚为什么在这行代码中:
complement (fromIntegral total + fromIntegral (total `shiftR` 16))
应该包括+ fromIntegral (total `shiftR` 16)
。
注意:我已经通过wireshark验证校验和是否正确,如果我补充total + total `shiftR` 16
,就像在链接的代码行中一样。所以我知道这是正确的,我只是不明白为什么。
答案 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