2026-07-25 17:58:54 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"hash/crc32"
|
|
|
|
|
"math/rand/v2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2026-07-25 19:08:11 -07:00
|
|
|
etherBase = 0x88b5
|
2026-07-25 17:58:54 -07:00
|
|
|
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())
|
|
|
|
|
}
|
|
|
|
|
}},
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 23:03:31 -07:00
|
|
|
// 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.
|
2026-07-25 17:58:54 -07:00
|
|
|
type frameSpec struct {
|
2026-07-25 23:03:31 -07:00
|
|
|
refs [][]byte
|
|
|
|
|
crcFor []map[int]uint32
|
2026-07-25 19:08:11 -07:00
|
|
|
dstMAC [6]byte
|
|
|
|
|
srcMAC [6]byte
|
|
|
|
|
etherType uint16
|
|
|
|
|
sizes []int
|
|
|
|
|
maxSize int
|
2026-07-25 23:03:31 -07:00
|
|
|
maxPay int
|
2026-07-25 17:58:54 -07:00
|
|
|
}
|
|
|
|
|
|
2026-07-25 23:03:31 -07:00
|
|
|
func newFrameSpec(dst, src [6]byte, etherType uint16, sizes []int) *frameSpec {
|
2026-07-25 17:58:54 -07:00
|
|
|
maxSize := 0
|
|
|
|
|
for _, s := range sizes {
|
|
|
|
|
if s > maxSize {
|
|
|
|
|
maxSize = s
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-25 23:03:31 -07:00
|
|
|
f := &frameSpec{
|
2026-07-25 19:08:11 -07:00
|
|
|
dstMAC: dst,
|
|
|
|
|
srcMAC: src,
|
|
|
|
|
etherType: etherType,
|
|
|
|
|
sizes: sizes,
|
|
|
|
|
maxSize: maxSize,
|
2026-07-25 23:03:31 -07:00
|
|
|
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)
|
2026-07-25 17:58:54 -07:00
|
|
|
}
|
2026-07-25 23:03:31 -07:00
|
|
|
return f
|
2026-07-25 17:58:54 -07:00
|
|
|
}
|
|
|
|
|
|
2026-07-25 23:03:31 -07:00
|
|
|
func (f *frameSpec) prefill(buf []byte, patIdx int) {
|
2026-07-25 17:58:54 -07:00
|
|
|
copy(buf[0:6], f.dstMAC[:])
|
|
|
|
|
copy(buf[6:12], f.srcMAC[:])
|
2026-07-25 19:08:11 -07:00
|
|
|
binary.BigEndian.PutUint16(buf[12:14], f.etherType)
|
2026-07-25 23:03:31 -07:00
|
|
|
copy(buf[minFrame:], f.refs[patIdx])
|
2026-07-25 17:58:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|