mirror of
https://github.com/soypat/lneto.git
synced 2026-08-21 06:59:04 +00:00
ethernet: MTU semantic meaning corrections, add FCS logic (#28)
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user