ethernet: MTU semantic meaning corrections, add FCS logic (#28)

This commit is contained in:
Pat Whittingslow
2026-01-26 16:45:35 -03:00
committed by GitHub
parent 6a6097a9ac
commit 85f018a02c
10 changed files with 241 additions and 25 deletions
+47
View File
@@ -0,0 +1,47 @@
package ethernet
import (
"encoding/binary"
"hash/crc32"
)
//
// CRC API.
//
// crcTable is the IEEE CRC-32 table used for Ethernet FCS calculation.
var crcTable = crc32.MakeTable(crc32.IEEE)
// CRC32 calculates the Ethernet Frame Check Sequence (FCS) for the given data.
// The CRC is computed using the IEEE 802.3 CRC-32 polynomial.
// The input should be the frame data from destination MAC through payload,
// excluding any existing FCS.
func CRC32(data []byte) uint32 {
return crc32.Checksum(data, crcTable)
}
// CRC32Search searches for a valid CRC32 in data starting from minOffCRC.
// It computes the CRC incrementally from minOffCRC, checking at each position
// if the CRC matches the next 4 bytes (little-endian FCS).
// Returns the offset where a valid CRC was found, or -1 if no valid CRC exists.
// This is useful when the exact frame length is unknown but bounded by minOffCRC.
func CRC32Search(data []byte, minOffCRC int) (foundOffOrNegative int) {
if minOffCRC < 0 {
minOffCRC = 0
}
if len(data) < minOffCRC+4 {
return -1
}
// Calculate CRC up to minOffCRC
crc := crc32.Checksum(data[:minOffCRC], crcTable)
// Incrementally extend and check at each position
for off := minOffCRC; off <= len(data)-4; off++ {
got := binary.LittleEndian.Uint32(data[off:])
if crc == got {
return off
}
// Extend CRC by one byte for next iteration
crc = crc32.Update(crc, crcTable, data[off:off+1])
}
return -1
}
+100
View File
@@ -0,0 +1,100 @@
package ethernet
import (
"encoding/binary"
"testing"
)
func TestCRC32Search(t *testing.T) {
// Helper to create test data with CRC appended at a specific position.
makeDataWithCRC := func(payloadLen int) []byte {
data := make([]byte, payloadLen+4)
for i := range data[:payloadLen] {
data[i] = byte(i)
}
crc := CRC32(data[:payloadLen])
binary.LittleEndian.PutUint32(data[payloadLen:], crc)
return data
}
t.Run("finds CRC at end", func(t *testing.T) {
data := makeDataWithCRC(100)
off := CRC32Search(data, 0)
if off != 100 {
t.Errorf("expected offset 100, got %d", off)
}
})
t.Run("finds CRC with minOff before CRC", func(t *testing.T) {
data := makeDataWithCRC(100)
off := CRC32Search(data, 50)
if off != 100 {
t.Errorf("expected offset 100, got %d", off)
}
})
t.Run("finds CRC with minOff exactly at CRC", func(t *testing.T) {
data := makeDataWithCRC(100)
off := CRC32Search(data, 100)
if off != 100 {
t.Errorf("expected offset 100, got %d", off)
}
})
t.Run("returns -1 when minOff past CRC", func(t *testing.T) {
data := makeDataWithCRC(100)
off := CRC32Search(data, 101)
if off != -1 {
t.Errorf("expected -1, got %d", off)
}
})
t.Run("returns -1 when no valid CRC", func(t *testing.T) {
data := make([]byte, 100)
for i := range data {
data[i] = byte(i)
}
off := CRC32Search(data, 0)
if off != -1 {
t.Errorf("expected -1, got %d", off)
}
})
t.Run("returns -1 when data too short", func(t *testing.T) {
data := []byte{1, 2, 3}
off := CRC32Search(data, 0)
if off != -1 {
t.Errorf("expected -1, got %d", off)
}
})
t.Run("handles negative minOff", func(t *testing.T) {
data := makeDataWithCRC(20)
off := CRC32Search(data, -5)
if off != 20 {
t.Errorf("expected offset 20, got %d", off)
}
})
t.Run("finds CRC at position 0", func(t *testing.T) {
// Empty payload, just CRC
data := make([]byte, 4)
crc := CRC32(nil)
binary.LittleEndian.PutUint32(data, crc)
off := CRC32Search(data, 0)
if off != 0 {
t.Errorf("expected offset 0, got %d", off)
}
})
t.Run("finds first valid CRC when multiple could match", func(t *testing.T) {
// Create data where CRC is at position 50
data := makeDataWithCRC(50)
// Extend with more bytes (the search should still find first match)
data = append(data, make([]byte, 50)...)
off := CRC32Search(data, 0)
if off != 50 {
t.Errorf("expected offset 50 (first match), got %d", off)
}
})
}
+6
View File
@@ -5,6 +5,12 @@ import (
)
const (
// MaxOverheadSize is the maximum overhead a packet can incur
// from transmitting an ethernet frame. Includes:
// - 14 bytes of Ethernet header containing MAC addresses and ethernet type, always present
// - 4 bytes of VLAN tag, if present.
// - 4 bytes of the 32 bit trailing CRC, if required by PHY.
MaxOverheadSize = 14 + 4 + 4
sizeHeaderNoVLAN = 14
)