C - CRC32 校验和与以太网帧校验序列上的 Wireshark 不匹配

Dan*_*son 1 c network-programming ethernet wireshark

我正在使用在线 CRC-32 计算器来检查我的输出是否正确,但是 Wireshark 似乎对以太网数据包具有不同的预期 FCS。

message2 是以太网帧减去 FCS,如 Wireshark 中所示

#include <stdio.h>
#include <stdint.h>

unsigned int crc32b(unsigned char *message) {
   int i, j;
   unsigned int byte, crc, mask;

   i = 0;
   crc = 0xFFFFFFFF;
   while (message[i] != 0) {
      //printf("%i %x \n\n", i, message[i]);
      byte = message[i];            
      crc = crc ^ byte;
      for (j = 7; j >= 0; j--) {  
         mask = -(crc & 1);
         crc = (crc >> 1) ^ (0xEDB88320 & mask);
      }
      i = i + 1;
   }
   return ~crc;
}

int main(void)
{
    unsigned char * message = "hello test";
    unsigned char * message2 = "aabbccddeeff5cb9017c5a53080000000000000000000000000000";
    unsigned int res = crc32b(message2);
    printf("%x\n", res);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用 [1 - subsection CRC-32 IEEE 802.3] 中定义的不同多项式,但是结果与 Wireshark 不匹配。

使用 0xED 多项式的输出:0xd81e4af3

Wireshark FCS 预期:0xa8cd3084

我真的很想在 FCS 中为我​​的 ethhdr 数据包编码,我猜在创建软件数据包时,NIC 没有输入 FCS ...

资料来源:

[1] - http://crppit.epfl.ch/documentation/Hash_Function/WiKi/Cyclic_redundancy_check.htm

krj*_*dev 5

您的实现绝对正确(对于 NUL 终止的 C 字符串)。这可能是网络接口的错误配置。在默认模式下,Wireshark 不会从网络驱动程序获取 FCS。如果您使用 Linux 并且驱动程序支持此功能,则必须使用ethtool启用此功能以获取 FCS。

不幸的是,在我的系统上,这只适用于接收帧:

$ ethtool -K eth0 rx-fcs on
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅内容。

我在嵌入式(用于 AVR 微控制器)项目中使用了稍微不同的算法,它对我来说非常适合:

#define CRC_POLY    0xEDB88320

uint32_t crc32_calc(uint8_t *data, int len)
{
    int i, j;
    uint32_t crc;

    if (!data)
        return 0;

    if (len < 1)
        return 0;

    crc = 0xFFFFFFFF;

    for (j = 0; j < len; j++) {
        crc ^= data[j];

        for (i = 0; i < 8; i++) {
             crc = (crc & 1) ? ((crc >> 1) ^ CRC_POLY) : (crc >> 1);
        }
    }

    return (crc ^ 0xFFFFFFFF);
}
Run Code Online (Sandbox Code Playgroud)

一个真实世界的例子:

Wireshark 中的以太网帧(使用 ethtool rx-fcs):

线鲨框架

我使用的实现的测试:

uint8_t frame[] = { 0x20, 0xcf, 0x30, 0x1a, 0xce, 0xa1, 0x62, 0x38, 
                    0xe0, 0xc2, 0xbd, 0x30, 0x08, 0x06, 0x00, 0x01, 
                    0x08, 0x00 ,0x06 ,0x04 ,0x00 ,0x01 ,0x62 ,0x38, 
                    0xe0 ,0xc2 ,0xbd ,0x30 ,0x0a, 0x2a, 0x2a, 0x01, 
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x2a, 
                    0x2a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                    0x00, 0x00, 0x00, 0x00 };


printf("0x%x\n", crc32_calc(frame, sizeof(frame)));
Run Code Online (Sandbox Code Playgroud)

输出:

$ ./fcs-test 
0x6026b722
$
Run Code Online (Sandbox Code Playgroud)

您可以看到,Wireshark 将0x22bf2660报告为正确的 FCS。由于字节顺序,这里只是一个不同的输出。但是CRC计算算法是正确的。

编辑:

我已经修改了你的代码:

uint32_t crc32b(uint8_t *message, int len) {
    int i, j;
    uint32_t crc, mask;
    uint8_t byte;

    crc = 0xFFFFFFFF;

    for (j = 0; j < len; j++) {
        byte = message[j];
        crc = crc ^ byte;
        for (i = 7; i >= 0; i--) {
            mask = -(crc & 1);
            crc = (crc >> 1) ^ (0xEDB88320 & mask);
        }
    }

    return ~crc;
}
Run Code Online (Sandbox Code Playgroud)

我添加了一个长度参数,因为您的实现仅在消息是 NUL 终止的 C 字符串时才能正常工作。如果您的输入是一个字节数组,那么您会得到一个不正确的 CRC 值。

查看差异(数组和 C 字符串):

uint8_t msg_arr[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x5c, 0xb9, 0x01, 0x7c, 0x5a, 0x53, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

char *msg_str = "aabbccddeeff5cb9017c5a53080000000000000000000000000000";

printf("0x%x\n", crc32b(msg_arr, sizeof(msg_arr)));
printf("0x%x\n", crc32b(msg_str, strlen(msg_str)));
Run Code Online (Sandbox Code Playgroud)

输出:

$
0x3422dd71
0xd81e4af3
$
Run Code Online (Sandbox Code Playgroud)