Java Wireshark校验和不匹配

Java Wireshark校验和不匹配,java,tcp,wireshark,checksum,Java,Tcp,Wireshark,Checksum,我编写了一个函数,用于计算给定tcp数据包的校验和。但是,当我捕获通过ipv4从wireshark发送的tcp数据包并让我的函数计算其校验和时,它的校验和就与wireshark捕获的数据包中的校验和不一样了。我检查了一下,我给computeChecksum函数的字节与我用wireshark捕获的tcp数据包字节完全相同 我根据公式计算了校验和。有人看到我的代码有什么错误吗 public long computeChecksum( byte[] buf, int src, int dst ){

我编写了一个函数,用于计算给定tcp数据包的校验和。但是,当我捕获通过ipv4从wireshark发送的tcp数据包并让我的函数计算其校验和时,它的校验和就与wireshark捕获的数据包中的校验和不一样了。我检查了一下,我给computeChecksum函数的字节与我用wireshark捕获的tcp数据包字节完全相同

我根据公式计算了校验和。有人看到我的代码有什么错误吗

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;
}
public long computeChecksum(字节[]buf,int src,int dst){
int length=buf.length;//tcppacket的总字节数。
int pseudoHeaderLength=12;//pseudoheader的字节数。
int i=0;
长和=0;
长数据;
buf[16]=(字节)0x0;//将校验和设置为0字节
buf[17]=(字节)0x0;
//按照rfc中的指定创建伪标头。
ByteBuffer伪HeaderByteBuffer=ByteBuffer.allocate(12);
伪headerByteBuffer.putInt(src);
伪headerByteBuffer.putInt(dst);
pseudoHeaderByteBuffer.put((字节)0x0);//存储0x0字节
pseudoHeaderByteBuffer.put((字节)PROTO_NUM_TCP);//存储协议号
pseudoHeaderByteBuffer.putShort((short)length);//存储数据包的长度。
字节[]pbuf=pseudoHeaderByteBuffer.array();
//循环通过psuedo头的所有16位字
int byteslight=伪头长度;
while(字节左>0){
//将字节存储在数据中的pbuf[i]和pbuf[i+1]处。
数据=((pbuf[i]0){
sum=sum&0xFFFF;//丢弃除16个最低有效位以外的所有位。
sum+=1;//加1(因为我们必须做一个1的补码求和,将进位加到求和中)。
}
i+=2;//指向下两个字节。
字节左-=2;
}
//循环遍历TCP数据包的所有16位字(即,直到只剩下1或0个字节)。
字节左=长度;
i=0;
而(bytesLeft>1){//注意,使用伪头,我们永远不会有剩余的奇数字节。
//我们的操作与伪报头完全相同,但对于TCP数据包字节。
数据=((buf[i]0){
sum=sum&0xFFFF;
总和+=1;
}
i+=2;
字节左-=2;
}
//如果数据的字节数为奇数,那么在添加所有16位字之后,我们将保留8位。
//在这种情况下,缺失的8位被视为全部0位。
如果(bytesleet>0){//,即剩余8位数据。
总和+=(buf[i]0){
sum=sum&0xFFFF;
总和+=1;
}
}
sum=~sum;//翻转所有位(即按照rfc的说明取一的补码)
sum=sum&0xFFFF;//仅保留16个最低有效位。
回报金额;
}

如果您没有发现代码有任何问题,请也告诉我。在这种情况下,我知道可以到其他地方查找问题。

我已经测试了您的代码,并且它工作正常。我已完成以下操作:

  • 将wireshark配置为“如果可能,验证TCP校验和”,以避免使用校验和不正确的数据包进行测试

  • 将长类型后缀
    L
    添加到常量
    0xffffff0000
    ,以避免编译时错误
    整数过大(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;
    }
    
通过此操作,我获得了一个校验和
C67C
,与wireshark中的校验和相同。

注意:当你这样做时,你的代码中有一个错误

pseudoHeaderByteBuffer.putShort( (short) length );

您将长度存储在伪报头中的两位补码中,如果长度大于2^15,这将是一个问题。您最好使用16位无符号的
char

这可能是endian问题吗?我不认为这是因为字节顺序不重要。最后,您只需添加所有字节,当总和结束时将添加1的16位流到前端。更改endiannes只意味着总和在不同的时间溢出。但它可能需要以不同的字节顺序放入数据包中。但是,无论数据包中字节的顺序如何,校验和不都是相同的吗?正如我所见,如果计算数据包中字节的校验和,然后将其随机移动按你想要的顺序排列字节,然后再次计算校验和,你应该得到完全相同的校验和。或者我错了吗?或者你的意思是校验和需要以不同的顺序排列?我不这么认为,因为我根本不把校验和放回数据包。我相信如果你对所有2字节的集合重新排序,它会是一样的(因为你在做16位加法),所以这两个字节的顺序可能不同(我相信网络使用的是little endian,所以它应该是
data=(pbuff[i]| pbuff[i+1]
pseudoHeaderByteBuffer.putShort( (short) length );