C - CRC32 校验和与以太网帧校验序列上的 Wireshark 不匹配
C - CRC32 checksum does not match Wireshark on Ethernet Frame Check Sequence
我正在使用在线 CRC-32 计算器来检查我的输出是否正确,但是 Wireshark 似乎对以太网数据包有不同的预期 FCS。
message2 是以太网帧减去 Wireshark 中看到的 FCS
#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;
}
我尝试使用 [1 - CRC-32 IEEE 802.3 小节] 中定义的不同多项式,但结果与 Wireshark 不匹配。
使用 0xED 多项式的输出:0xd81e4af3
Wireshark FCS 预期:0xa8cd3084
我真的很想在 FCS 中为我的 ethhdr 数据包编码,我想在创建软件包时,FCS 不是由 NIC 输入的...
来源:
[1] - http://crppit.epfl.ch/documentation/Hash_Function/WiKi/Cyclic_redundancy_check.htm
你的代码有很多问题。您还可以比较许多现有的实现(例如,this one 从 CRC 上的真实维基百科页面链接)。
unsigned char * message2 = "aabbccddeeff5cb9017c5a53080000000000000000000000000000";
您是否希望这是您在 Wireshark 中看到的八位字节序列 0xAA 0xBB 0xCC ...
?因为那根本不是你所拥有的。
这个字符串实际上包含 0x61 0x61 0x62 0x62 ...
(假设您的平台使用 ASCII 编码)因为它是一个字符串而不是八位字节串。
- 具体来说,这里:
byte = message[i];
你假设你的消息的前 8 位是一个八位字节,我再次假设,因为你没有说,你预计这是 0xAA
。它实际上是 0x61
.
如果您希望它正常工作,请使用 strtoul(pair, NULL, 16)
或类似的方法将每对字符转换为整数值。
你有一个循环 for (j = 7; j >= 0; j--)
但从不在其中使用 j
。您 do 以一种看起来很奇怪的方式使用整数常量 1
:是否应该有 (1 << j)
或其他东西?
我建议修复明显的错误,然后在尝试使用 Wireshark 比较整个帧之前编写一些独立的测试。您发布的代码有一些基本错误,应该在您达到这一点之前进行测试、识别和修复。
我不确定你的问题,但如果你想计算网络数据包的校验和,你必须以正确的结构部署数据。
请确保您的问题与字节序无关。
网络byte-order是big-endian,这里是事情变得有点难的地方。
Little-Endian 主要用于 PC,但可能因硬件和制造商而异。
2 字节整数(16 位整数),值为 255。
- 小字节序:FF00
- 大字节序:00FF
我不确定您要匹配的校验和类型,但校验和不仅用于数据字段,而且大部分包含所有标志和选项,在最后一步发出,这就是实现需要相应数据结构的原因。
关于校验和,当你使用wireshark得到一个无效的校验和时,有很多情况,它可能会导致内核、虚拟适配器、加速网络、你的NIC中的专用CPU等...
TCP 示例 header:
/* include/linux/tcp.h */
struct tcphdr {
__u16 source;
__u16 dest;
__u32 seq;
__u32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__u16 window;
__u16 check;
__u16 urg_ptr;
};
您的实现完全正确(对于以 NUL 结尾的 C 字符串)。这可能是网络接口配置错误。在默认模式下,Wireshark 不会从网络驱动程序中获取 FCS。如果您使用 Linux 并且驱动程序支持它,那么您必须使用 ethtool 启用它以获取 FCS。
不幸的是,在我的系统上这只适用于接收帧:
$ ethtool -K eth0 rx-fcs on
详情见this。
我在我的嵌入式(用于 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);
}
一个真实世界的例子:
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)));
输出:
$ ./fcs-test
0x6026b722
$
您可以看到,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;
}
我添加了一个长度参数,因为您的实现只有在消息是 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)));
输出:
$
0x3422dd71
0xd81e4af3
$
我正在使用在线 CRC-32 计算器来检查我的输出是否正确,但是 Wireshark 似乎对以太网数据包有不同的预期 FCS。
message2 是以太网帧减去 Wireshark 中看到的 FCS
#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;
}
我尝试使用 [1 - CRC-32 IEEE 802.3 小节] 中定义的不同多项式,但结果与 Wireshark 不匹配。
使用 0xED 多项式的输出:0xd81e4af3
Wireshark FCS 预期:0xa8cd3084
我真的很想在 FCS 中为我的 ethhdr 数据包编码,我想在创建软件包时,FCS 不是由 NIC 输入的...
来源:
[1] - http://crppit.epfl.ch/documentation/Hash_Function/WiKi/Cyclic_redundancy_check.htm
你的代码有很多问题。您还可以比较许多现有的实现(例如,this one 从 CRC 上的真实维基百科页面链接)。
unsigned char * message2 = "aabbccddeeff5cb9017c5a53080000000000000000000000000000";
您是否希望这是您在 Wireshark 中看到的八位字节序列
0xAA 0xBB 0xCC ...
?因为那根本不是你所拥有的。这个字符串实际上包含
0x61 0x61 0x62 0x62 ...
(假设您的平台使用 ASCII 编码)因为它是一个字符串而不是八位字节串。- 具体来说,这里:
byte = message[i];
你假设你的消息的前 8 位是一个八位字节,我再次假设,因为你没有说,你预计这是0xAA
。它实际上是0x61
.
如果您希望它正常工作,请使用
strtoul(pair, NULL, 16)
或类似的方法将每对字符转换为整数值。- 具体来说,这里:
你有一个循环
for (j = 7; j >= 0; j--)
但从不在其中使用j
。您 do 以一种看起来很奇怪的方式使用整数常量1
:是否应该有(1 << j)
或其他东西?
我建议修复明显的错误,然后在尝试使用 Wireshark 比较整个帧之前编写一些独立的测试。您发布的代码有一些基本错误,应该在您达到这一点之前进行测试、识别和修复。
我不确定你的问题,但如果你想计算网络数据包的校验和,你必须以正确的结构部署数据。
请确保您的问题与字节序无关。 网络byte-order是big-endian,这里是事情变得有点难的地方。
Little-Endian 主要用于 PC,但可能因硬件和制造商而异。
2 字节整数(16 位整数),值为 255。
- 小字节序:FF00
- 大字节序:00FF
我不确定您要匹配的校验和类型,但校验和不仅用于数据字段,而且大部分包含所有标志和选项,在最后一步发出,这就是实现需要相应数据结构的原因。
关于校验和,当你使用wireshark得到一个无效的校验和时,有很多情况,它可能会导致内核、虚拟适配器、加速网络、你的NIC中的专用CPU等...
TCP 示例 header:
/* include/linux/tcp.h */
struct tcphdr {
__u16 source;
__u16 dest;
__u32 seq;
__u32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__u16 window;
__u16 check;
__u16 urg_ptr;
};
您的实现完全正确(对于以 NUL 结尾的 C 字符串)。这可能是网络接口配置错误。在默认模式下,Wireshark 不会从网络驱动程序中获取 FCS。如果您使用 Linux 并且驱动程序支持它,那么您必须使用 ethtool 启用它以获取 FCS。
不幸的是,在我的系统上这只适用于接收帧:
$ ethtool -K eth0 rx-fcs on
详情见this。
我在我的嵌入式(用于 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);
}
一个真实世界的例子:
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)));
输出:
$ ./fcs-test
0x6026b722
$
您可以看到,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;
}
我添加了一个长度参数,因为您的实现只有在消息是 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)));
输出:
$
0x3422dd71
0xd81e4af3
$