CRC-32 코드를 직접 작성했을 때 제일 먼저 필요한 것은 믿을 수 있는 테스트 벡터다.
가장 널리 쓰는 입력은 문자열 123456789다.
이 입력을 Ethernet CRC-32 방식으로 계산하면 결과는 다음이다.
Input: 123456789
CRC-32 value: 0xCBF43926
FCS bytes on wire: 26 39 F4 CB
이 글은 이 값을 기준으로 CRC-32 구현을 빠르게 검증하는 방법을 정리한다.
먼저 CRC-32 variant를 고정해야 한다
CRC는 이름이 비슷해도 variant가 다르면 결과가 달라진다.
Ethernet FCS에서 쓰는 CRC-32는 보통 다음 파라미터로 구현한다.
Width: 32
Polynomial: 0x04C11DB7
Reflected polynomial: 0xEDB88320
Initial value: 0xFFFFFFFF
Final XOR: 0xFFFFFFFF
Input reflected: true
Result reflected: true
테이블 기반 구현이든 bit-by-bit 구현이든, 이 조건이 맞아야 123456789 -> 0xCBF43926이 나온다.
만약 결과가 다르다면 코드가 틀렸을 수도 있지만, 사실은 다른 CRC variant를 계산하고 있을 가능성도 크다.
간단한 C 구현 예제
아래 코드는 table 없이 bit 단위로 계산하는 단순한 예제다.
속도는 빠르지 않지만 검증용으로 읽기 쉽다.
#include <stdint.h>
#include <stddef.h>
uint32_t ethernet_crc32(const uint8_t *data, size_t length)
{
uint32_t crc = 0xFFFFFFFFu;
for (size_t i = 0; i < length; i++) {
crc ^= data[i];
for (int bit = 0; bit < 8; bit++) {
if (crc & 1u) {
crc = (crc >> 1) ^ 0xEDB88320u;
} else {
crc >>= 1;
}
}
}
return crc ^ 0xFFFFFFFFu;
}
테스트 입력은 다음처럼 넣을 수 있다.
const uint8_t test[] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9'
};
uint32_t crc = ethernet_crc32(test, sizeof(test));
이때 crc는 0xCBF43926이어야 한다.
FCS byte로 바꾸기
Ethernet frame 끝에 붙는 FCS는 32-bit 값을 사람이 읽는 순서 그대로 쓰지 않는다.
CRC 값이 0xCBF43926이면 FCS byte는 다음 순서다.
26 39 F4 CB
C 코드로는 이렇게 나눌 수 있다.
uint8_t fcs[4];
fcs[0] = (uint8_t)(crc & 0xFFu);
fcs[1] = (uint8_t)((crc >> 8) & 0xFFu);
fcs[2] = (uint8_t)((crc >> 16) & 0xFFu);
fcs[3] = (uint8_t)((crc >> 24) & 0xFFu);
즉 lower byte부터 frame에 붙인다.
이 부분을 big-endian으로 처리하면 CRC 값은 맞는데 실제 frame check가 실패하는 묘한 상태가 된다.
hex input으로 같은 값 확인하기
문자열 123456789의 ASCII hex는 다음이다.
31 32 33 34 35 36 37 38 39
이 byte들을 CRC 계산에 넣으면 결과는 동일하다.
CRC-32 value: 0xCBF43926
FCS bytes on wire: 26 39 F4 CB
따라서 text mode와 hex byte mode에서 같은 결과가 나오는지 비교해보면, 문자열 인코딩 문제까지 같이 확인할 수 있다.
captured FCS 검증 예제
이미 FCS가 붙어 있는 frame을 검증할 때는 마지막 4바이트를 계산 입력에서 제외해야 한다.
예를 들어 아래 byte sequence가 있다고 하자.
31 32 33 34 35 36 37 38 39 26 39 F4 CB
여기서:
payload bytes:
31 32 33 34 35 36 37 38 39
captured FCS:
26 39 F4 CB
앞의 9바이트만 CRC 계산에 넣으면 expected FCS가 26 39 F4 CB로 나온다.
마지막 4바이트까지 같이 CRC 입력에 넣어놓고 “결과가 안 맞는다”고 보면 안 된다.
검증 모드에서는 frame body와 trailer를 분리해서 봐야 한다.
구현이 틀릴 때 자주 나오는 결과
CRC 코드를 직접 만들면 다음 실수가 자주 나온다.
1. polynomial 방향을 반대로 씀
bit를 오른쪽으로 shift하는 reflected 구현에서는 보통 0xEDB88320을 쓴다.
왼쪽 shift 방식의 구현 예제를 가져와 섞으면 결과가 달라진다.
2. initial value를 0으로 시작함
일부 CRC 예제는 init 값을 0x00000000으로 둔다.
Ethernet FCS 기준에서는 0xFFFFFFFF로 시작해야 한다.
3. final XOR를 빼먹음
루프 계산이 끝난 뒤 crc ^ 0xFFFFFFFF를 반환해야 한다.
이 final XOR가 빠지면 테스트 벡터가 맞지 않는다.
4. FCS byte order를 반대로 붙임
0xCBF43926을 CB F4 39 26으로 붙이면 안 된다.
Ethernet frame trailer에는 26 39 F4 CB 순서로 붙는다.
도구로 빠르게 교차 검증하기
내 구현이 맞는지 확인할 때는 Ethernet CRC and FCS Calculator에 같은 입력을 넣어 비교하면 빠르다.
확인할 값은 두 가지다.
- 코드가 반환한
CRC-32 value - frame 끝에 붙이는
FCS bytes on wire
두 값이 모두 맞아야 실제 Ethernet frame 생성이나 MAC 검증에서 헷갈리지 않는다.
한 줄 요약
CRC-32 구현 검증은 123456789 -> 0xCBF43926으로 시작하고, Ethernet frame에 붙일 FCS는 그 값을 lower byte first로 나눈 26 39 F4 CB인지까지 확인해야 한다.