package main import ( "encoding/binary" "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 patternNames() []string { names := make([]string, len(patterns)) for i, p := range patterns { names[i] = p.name } return names } // Every pattern is laid out at full length up front with its checksum at each // size, so a sender picks a pattern by choosing a buffer, never by writing one. type frameSpec struct { refs [][]byte crcFor []map[int]uint32 dstMAC [6]byte srcMAC [6]byte etherType uint16 sizes []int maxSize int maxPay int } func newFrameSpec(dst, src [6]byte, etherType uint16, sizes []int) *frameSpec { maxSize := 0 for _, s := range sizes { if s > maxSize { maxSize = s } } f := &frameSpec{ dstMAC: dst, srcMAC: src, etherType: etherType, sizes: sizes, maxSize: maxSize, maxPay: maxSize - minFrame, } for _, p := range patterns { ref := make([]byte, f.maxPay) p.fill(ref) crcFor := make(map[int]uint32, len(sizes)) for _, s := range sizes { crcFor[s] = crc32.Checksum(ref[:s-minFrame], crcTable) } f.refs = append(f.refs, ref) f.crcFor = append(f.crcFor, crcFor) } return f } func (f *frameSpec) prefill(buf []byte, patIdx int) { copy(buf[0:6], f.dstMAC[:]) copy(buf[6:12], f.srcMAC[:]) binary.BigEndian.PutUint16(buf[12:14], f.etherType) copy(buf[minFrame:], f.refs[patIdx]) } 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 }