176 lines
3.3 KiB
Go
176 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"hash/crc32"
|
|
"math/rand/v2"
|
|
)
|
|
|
|
const (
|
|
etherType = 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
|
|
sizes []int
|
|
maxSize int
|
|
}
|
|
|
|
func newFrameSpec(patIdx int, dst, src [6]byte, 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,
|
|
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], 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
|
|
}
|