package main import ( "encoding/binary" "hash/crc32" "math/rand/v2" ) const ( etherBase = 0x88b5 ethHdrLen = 14 hdrLen = 20 hdrMagic = 0x43424c54 hdrVer = 1 minFrame = ethHdrLen + hdrLen maxFrame = 9216 hdrCheckOff = 18 ) var crcTable = crc32.MakeTable(crc32.Castagnoli) var patterns = []func([]byte){ func(b []byte) { clear(b) }, func(b []byte) { for i := range b { b[i] = 0xff } }, func(b []byte) { for i := range b { if i%2 == 0 { b[i] = 0x55 } else { b[i] = 0xaa } } }, func(b []byte) { for i := range b { b[i] = byte(i) } }, func(b []byte) { s := uint64(0x0123456789abcdef) for i := range b { s ^= s << 13 s ^= s >> 7 s ^= s << 17 b[i] = byte(s) } }, func(b []byte) { r := rand.New(rand.NewPCG(0xc0ffee, 0x5eed)) for i := range b { b[i] = byte(r.Uint32()) } }, } // 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 _, fill := range patterns { ref := make([]byte, f.maxPay) fill(ref) crcFor := make(map[int]uint32, len(sizes)) for _, s := range sizes { crcFor[s-minFrame] = 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]) } // Looked up rather than read from the frame: a checksum travelling beside the // bytes it covers is only ever compared against itself. func (f *frameSpec) expectedCRC(patIdx, payLen int) (uint32, bool) { if patIdx < 0 || patIdx >= len(f.crcFor) { return 0, false } crc, ok := f.crcFor[patIdx][payLen] return crc, ok } // A one's complement sum over the header, skipping the two bytes it is written // into. The payload checksum covers the payload alone, so without this the // sequence number is the one field on the wire that nothing checks, and a bit // flipped in it arrives looking exactly like a legitimate frame from far in the // future. Every single bit error changes the folded sum, which is the error a // cable going marginal actually produces. func headerCheck(h []byte) uint16 { var sum uint32 for i := 0; i+1 < hdrLen; i += 2 { if i == hdrCheckOff { continue } sum += uint32(binary.BigEndian.Uint16(h[i:])) } for sum>>16 != 0 { sum = sum&0xffff + sum>>16 } return ^uint16(sum) } func putHeader(buf []byte, patIdx int, stream uint16, seq uint64, payLen int) { h := buf[ethHdrLen:] binary.BigEndian.PutUint32(h[0:4], hdrMagic) h[4] = hdrVer 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[hdrCheckOff:], headerCheck(h)) } type parsed struct { patIdx int stream uint16 seq uint64 payLen int } // Why a frame was turned away. A header that is not ours and a header of ours // that arrived damaged are different things to report: the first is somebody // else on the wire, the second is the fault being tested for. type hdrStatus int const ( hdrOK hdrStatus = iota hdrForeign hdrDamaged ) func parseHeader(buf []byte) (parsed, hdrStatus) { var p parsed if len(buf) < minFrame { return p, hdrForeign } h := buf[ethHdrLen:] if binary.BigEndian.Uint32(h[0:4]) != hdrMagic { return p, hdrForeign } if binary.BigEndian.Uint16(h[hdrCheckOff:]) != headerCheck(h) { return p, hdrDamaged } // Both halves are the same build, so the only way this differs is a checksum // that happened to agree over damaged bytes. if h[4] != hdrVer { return p, hdrDamaged } 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])) if minFrame+p.payLen > len(buf) { return p, hdrDamaged } return p, hdrOK }