我编写了一个函数来计算给定tcp数据包的校验和。但是,当我从wireshark捕获通过ipv4发送的tcp数据包并让我的函数计算其校验和时,那么它与wireshark捕获数据包中的校验和不同。我检查过,我给computeChecksum函数的字节与用wireshark捕获的tcp包字节完全相同。
我根据RFC 793计算了校验和。有人看到我的代码中有什么问题吗?
public long computeChecksum( byte[] buf, int src, int dst ){
int length = buf.length; // nr of bytes of the tcppacket in total.
int pseudoHeaderLength = 12; // nr of bytes of pseudoheader.
int i = 0;
long sum = 0;
long data;
buf[16] = (byte)0x0; // set checksum to 0 bytes
buf[17] = (byte)0x0;
// create the pseudoheader as specified in the rfc.
ByteBuffer pseudoHeaderByteBuffer = ByteBuffer.allocate( 12 );
pseudoHeaderByteBuffer.putInt( src );
pseudoHeaderByteBuffer.putInt( dst );
pseudoHeaderByteBuffer.put( (byte)0x0 ); // store the 0x0 byte
pseudoHeaderByteBuffer.put( (byte)PROTO_NUM_TCP ); // stores the protocol number
pseudoHeaderByteBuffer.putShort( (short) length ); // store the length of the packet.
byte[] pbuf = pseudoHeaderByteBuffer.array();
// loop through all 16-bit words of the psuedo header
int bytesLeft = pseudoHeaderLength;
while( bytesLeft > 0 ){
// store the bytes at pbuf[i] and pbuf[i+1] in data.
data = ( ((pbuf[i] << 8) & 0xFF00) | ((pbuf[i + 1]) & 0x00FF));
sum += data;
// Check if the sum has bit 17 or higher set by doing a binary AND with the 46 most significant bits and 0xFFFFFFFFFF0000.
if( (sum & 0xFFFFFFFF0000) > 0 ){
sum = sum & 0xFFFF; // discard all but the 16 least significant bits.
sum += 1; // add 1 (because we have to do a one's complement sum where you add the carry bit to the sum).
}
i += 2; // point to the next two bytes.
bytesLeft -= 2;
}
// loop through all 16-bit words of the TCP packet (ie. until there's only 1 or 0 bytes left).
bytesLeft = length;
i=0;
while( bytesLeft > 1 ){ // note that with the pseudo-header we could never have an odd byte remaining.
// We do do exactly the same as with the pseudo-header but then for the TCP packet bytes.
data = ( ((buf[i] << 8) & 0xFF00) | ((buf[i + 1]) & 0x00FF));
sum += data;
if( (sum & 0xFFFF0000) > 0 ){
sum = sum & 0xFFFF;
sum += 1;
}
i += 2;
bytesLeft -= 2;
}
// If the data has an odd number of bytes, then after adding all 16 bit words we remain with 8 bits.
// In that case the missing 8 bits is considered to be all 0's.
if( bytesLeft > 0 ){ // ie. there are 8 bits of data remaining.
sum += (buf[i] << 8 & 0xFF00); // construct a 16 bit word holding buf[i] and 0x00 and add it to the sum.
if( (sum & 0xFFFF0000) > 0) {
sum = sum & 0xFFFF;
sum += 1;
}
}
sum = ~sum; // Flip all bits (ie. take the one's complement as stated by the rfc)
sum = sum & 0xFFFF; // keep only the 16 least significant bits.
return sum;
}
如果您没有看到代码有任何问题,请告诉我。在那种情况下,我知道在其他地方寻找问题。
答案 0 :(得分:1)
我已经测试了您的代码并且它可以正常运行。我做了以下事情:
将wireshark配置为&#34;如果可能,验证TCP校验和&#34;为了避免使用校验和错误的数据包进行测试。
将长类型后缀L
添加到常量0xFFFFFFFF0000
,以避免编译时错误integer number too large
(Java 8)。
使用来自wireshark的TCP段的十六进制表示
String tcpSegment = "0050dc6e5add5b4fa9bf9ad8a01243e0c67c0000020405b4010303000101080a00079999000d4e0e";
使用方法将十六进制字符串转换为字节数组
public static byte[] toByteArray(String strPacket) {
int len = strPacket.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(strPacket.charAt(i), 16) << 4)
+ Character.digit(strPacket.charAt(i + 1), 16));
}
return data;
}
使用ByteBuffer将源和目标地址写入int
int src = ByteBuffer.wrap(toByteArray("c0a80001")).getInt();
int dst = ByteBuffer.wrap(toByteArray("c0a8000a")).getInt();
有了这个,我获得了C67C
的校验和,与wireshark中的相同。
P.S。:当您执行
时,代码中出现错误pseudoHeaderByteBuffer.putShort( (short) length );
您将长度存储在伪标头内的两个补码中,如果长度大于2 ^ 15,这将是一个问题。您最好使用16位无符号的char
。