package main import ( "encoding/binary" "fmt" "hash/crc32" "math/rand/v2" ) const ( etherBase = 0x88b5 ethHdrLen = 14 hdrLen = 24 hdrMagic = 0x43424c54 minFrame = ethHdrLen + hdrLen maxFrame = 9216 ) var crcTable = crc32.MakeTable(crc32.Castagnoli) type pattern struct { name string fill func([]byte) } var patterns = []pattern{ {"zeros", func(b []byte) { clear(b) }}, {"ones", func(b []byte) { for i := range b { b[i] = 0xff } }}, {"alt", func(b []byte) { for i := range b { if i%2 == 0 { b[i] = 0x55 } else { b[i] = 0xaa } } }}, {"incr", func(b []byte) { for i := range b { b[i] = byte(i) } }}, {"prbs", func(b []byte) { s := uint64(0x0123456789abcdef) for i := range b { s ^= s << 13 s ^= s >> 7 s ^= s << 17 b[i] = byte(s) } }}, {"random", func(b []byte) { r := rand.New(rand.NewPCG(0xc0ffee, 0x5eed)) for i := range b { b[i] = byte(r.Uint32()) } }}, } func patternIndex(name string) (int, error) { for i, p := range patterns { if p.name == name { return i, nil } } names := make([]string, len(patterns)) for i, p := range patterns { names[i] = p.name } return 0, fmt.Errorf("unknown pattern %q (have %v)", name, names) } type frameSpec struct { patIdx int ref []byte crcFor map[int]uint32 dstMAC [6]byte srcMAC [6]byte etherType uint16 sizes []int maxSize int } func newFrameSpec(patIdx int, dst, src [6]byte, etherType uint16, sizes []int) *frameSpec { maxSize := 0 for _, s := range sizes { if s > maxSize { maxSize = s } } ref := make([]byte, maxSize-minFrame) patterns[patIdx].fill(ref) crcFor := make(map[int]uint32, len(sizes)) for _, s := range sizes { crcFor[s] = crc32.Checksum(ref[:s-minFrame], crcTable) } return &frameSpec{ patIdx: patIdx, ref: ref, crcFor: crcFor, dstMAC: dst, srcMAC: src, etherType: etherType, sizes: sizes, maxSize: maxSize, } } func (f *frameSpec) prefill(buf []byte) { copy(buf[0:6], f.dstMAC[:]) copy(buf[6:12], f.srcMAC[:]) binary.BigEndian.PutUint16(buf[12:14], f.etherType) copy(buf[minFrame:], f.ref) } func putHeader(buf []byte, patIdx int, stream uint16, seq uint64, payLen int, crc uint32) { h := buf[ethHdrLen:] binary.BigEndian.PutUint32(h[0:4], hdrMagic) h[4] = 1 h[5] = byte(patIdx) binary.BigEndian.PutUint16(h[6:8], stream) binary.BigEndian.PutUint64(h[8:16], seq) binary.BigEndian.PutUint16(h[16:18], uint16(payLen)) binary.BigEndian.PutUint16(h[18:20], 0) binary.BigEndian.PutUint32(h[20:24], crc) } type parsed struct { patIdx int stream uint16 seq uint64 payLen int crc uint32 } func parseHeader(buf []byte) (parsed, bool) { var p parsed if len(buf) < minFrame { return p, false } h := buf[ethHdrLen:] if binary.BigEndian.Uint32(h[0:4]) != hdrMagic { return p, false } p.patIdx = int(h[5]) p.stream = binary.BigEndian.Uint16(h[6:8]) p.seq = binary.BigEndian.Uint64(h[8:16]) p.payLen = int(binary.BigEndian.Uint16(h[16:18])) p.crc = binary.BigEndian.Uint32(h[20:24]) if minFrame+p.payLen > len(buf) { return p, false } return p, true } func firstDiff(got, want []byte) (int, int) { n := len(got) if len(want) < n { n = len(want) } for i := 0; i < n; i++ { if got[i] != want[i] { bits := 0 x := got[i] ^ want[i] for x != 0 { bits += int(x & 1) x >>= 1 } return i, bits } } return -1, 0 }