IPv4首部校验和计算结果与Wireshark不一致问题排查
IPv4首部校验和计算错误的问题分析与修正
核心问题:字节序不匹配
IPv4协议规定所有多字节字段(如总长度、标识、校验和等)必须使用大端字节序(网络字节序),但你创建ByteBuffer时指定了ByteOrder.LITTLE_ENDIAN,这会导致所有多字节字段的字节顺序反转,直接让校验和计算结果完全错误。
比如totalLength值为1056,对应十六进制0x0420,大端序下buffer中存储的是0x04、0x20,但小端序会存储为0x20、0x04,计算时取这两个字节组成的16位字变成0x2004,而非正确的0x0420,最终求和结果必然偏差。
次要问题:校验和字段初始化
IPv4校验和计算要求校验和字段本身必须先置为0,如果你的checksum变量初始值非0,即便计算时跳过了该字段,buffer中残留的非0值也会影响后续验证逻辑(虽不是本次错误的直接原因,但必须修正)。
代码修正步骤
1. 修正ByteBuffer字节序
将ByteBuffer的字节序改为大端(默认即为大端,显式声明可避免歧义):
var tmpBuffer = ByteBuffer.allocate(getSizeInBytes()).order(ByteOrder.BIG_ENDIAN);
2. 初始化校验和字段为0
构造buffer时,先将校验和字段设为0:
tmpBuffer.putShort((short) 0); // 校验和字段必须先置0
3. 优化校验和计算逻辑(提升正确性)
调整进位处理逻辑,确保所有进位都被累加,同时简化取反操作:
private short computeChecksum(byte[] data) { int end = getSizeInBytes(); // =20 int sum = 0; final int checksumStart = 10; // 校验和字段起始字节索引 for (int i = 0; i < end; i += 2) { if (i == checksumStart) { continue; // 跳过校验和字段 } // 组合成16位大端字:高字节左移8位 + 低字节 int word = ((data[i] & 0xFF) << 8) | (data[i+1] & 0xFF); sum += word; // 循环处理进位,直到sum仅保留低16位 while ((sum >> 16) != 0) { sum = (sum & 0xFFFF) + (sum >> 16); } } // 取反得到最终校验和 return (short) ~sum; }
验证结果
修正后重新计算,即可得到Wireshark显示的正确值0xdc5c。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

