mirror of
https://github.com/soypat/lneto.git
synced 2026-09-10 00:29:34 +00:00
Fuzz fixes (#73)
* remove old fuzz corpus and stick to using PCG for seeding fuzz test * polish up drain logic and reduce action search space * improve fuzzing debug prints * more fuzz fixes also formatter capture printer fixes * refactor StackSeeded test * fix challenge ack infinite packet bug * more expressive challenge satisfy * remove printing
This commit is contained in:
+360
-240
@@ -1,7 +1,10 @@
|
||||
package xnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"net/netip"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
@@ -182,258 +185,375 @@ func fixIPTCPCRCs(pkt []byte) (fixable bool) {
|
||||
}
|
||||
|
||||
func FuzzStackSeeded(f *testing.F) {
|
||||
f.Add(int64(1), int64(2), int64(3))
|
||||
var pmut ltesto.PacketMut
|
||||
f.Fuzz(func(t *testing.T, seed1, seed2, seedAction int64) {
|
||||
if seed1 == 0 {
|
||||
seed1++
|
||||
}
|
||||
if seed2 == 0 {
|
||||
seed2++
|
||||
}
|
||||
const mtu = 1500
|
||||
const mfl = mtu + 14 // frame length includes ethernet header
|
||||
var buf [mfl]byte
|
||||
var s1, s2 StackAsync
|
||||
v1, v2 := byte(seed1), byte(seed2)
|
||||
cfg1 := StackConfig{
|
||||
Hostname: "s1",
|
||||
StaticAddress: netip.AddrFrom4([4]byte{1, 0, 0, v1}),
|
||||
RandSeed: seed1,
|
||||
MaxActiveTCPPorts: 1,
|
||||
MaxActiveUDPPorts: 1,
|
||||
ICMPQueueLimit: 1 + int(v1%4),
|
||||
MTU: mtu,
|
||||
HardwareAddress: [6]byte{0x1, 0, 0, 0, 0, v1},
|
||||
AcceptMulticast: v1%2 == 0,
|
||||
}
|
||||
err := s1.Reset(cfg1)
|
||||
if err != nil {
|
||||
t.Fatal(err, cfg1)
|
||||
}
|
||||
cfg2 := StackConfig{
|
||||
Hostname: "s2",
|
||||
StaticAddress: netip.AddrFrom4([4]byte{1, 0, 0, v2}),
|
||||
RandSeed: seed2,
|
||||
MaxActiveTCPPorts: 1,
|
||||
MaxActiveUDPPorts: 1,
|
||||
ICMPQueueLimit: 1 + int(v2%4),
|
||||
MTU: mtu,
|
||||
HardwareAddress: [6]byte{0x2, 0, 0, 0, 0, v2},
|
||||
AcceptMulticast: v2%2 == 0,
|
||||
}
|
||||
err = s2.Reset(cfg2)
|
||||
if err != nil {
|
||||
t.Fatal(err, cfg2)
|
||||
}
|
||||
const maxActions = 100
|
||||
const (
|
||||
actionUDP = iota
|
||||
actionTCP
|
||||
actionICMP
|
||||
actionARP
|
||||
actionNone
|
||||
actionLim
|
||||
)
|
||||
const (
|
||||
pingMinPayload = 8
|
||||
port1 = 8080
|
||||
port2 = 80
|
||||
bufsize = 64
|
||||
)
|
||||
var udp1, udp2 udp.Conn
|
||||
var tcp1, tcp2 tcp.Conn
|
||||
err = tcp1.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
TxPacketQueueSize: 1 + int(s1.Prand32())%10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = tcp2.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
TxPacketQueueSize: 1 + int(s2.Prand32())%10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = udp1.Configure(udp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
RxQueueSize: int(1 + s1.Prand32()%10),
|
||||
TxQueueSize: int(1 + s1.Prand32()%10),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = udp2.Configure(udp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
RxQueueSize: int(1 + s2.Prand32()%10),
|
||||
TxQueueSize: int(1 + s2.Prand32()%10),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
icmpEnabled := false
|
||||
udpOrder := 0
|
||||
betsAreOff := false // When a packet is mutated all bets on which error can be returned are off.
|
||||
for i := 0; i < maxActions; i++ {
|
||||
action1 := s1.Prand32()
|
||||
switch action1 % actionLim {
|
||||
case actionTCP:
|
||||
state1 := tcp1.State()
|
||||
state2 := tcp2.State()
|
||||
if state1 == 0 && state2 == 0 {
|
||||
err = s1.DialTCP(&tcp1, port1, netip.AddrPortFrom(s2.Addr(), port2))
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
err = s2.ListenTCP(&tcp2, port2)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
} else if state1 == tcp.StateEstablished && state2 == tcp.StateEstablished {
|
||||
// For now just close after established.
|
||||
if s1.Prand32()%2 == 0 {
|
||||
tcp1.Close()
|
||||
} else {
|
||||
tcp2.Close()
|
||||
}
|
||||
}
|
||||
case actionUDP:
|
||||
// Ensure connections open.
|
||||
if !udp1.IsOpen() {
|
||||
err = s1.DialUDP(&udp1, port1, netip.AddrPortFrom(s2.Addr(), port2))
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
}
|
||||
if !udp2.IsOpen() {
|
||||
err = s2.DialUDP(&udp2, port2, netip.AddrPortFrom(s1.Addr(), port1))
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
}
|
||||
udpOrder++
|
||||
action := s1.Prand32() % 8
|
||||
switch action {
|
||||
case 0:
|
||||
if udp1.FreeOutput() > 0 {
|
||||
udp1.Write([]byte{byte(udpOrder)})
|
||||
}
|
||||
case 1:
|
||||
if udp1.BufferedInput() > 0 {
|
||||
udp1.Read(buf[:])
|
||||
}
|
||||
case 2:
|
||||
if udp2.FreeOutput() > 0 {
|
||||
udp2.Write([]byte{byte(udpOrder)})
|
||||
}
|
||||
case 3:
|
||||
if udp2.BufferedInput() > 0 {
|
||||
udp2.Read(buf[:])
|
||||
}
|
||||
case 4:
|
||||
udp1.Close()
|
||||
case 5:
|
||||
udp2.Close()
|
||||
}
|
||||
case actionICMP:
|
||||
if !icmpEnabled {
|
||||
err = s1.EnableICMP(true)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
err = s2.EnableICMP(true)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
icmpEnabled = true
|
||||
}
|
||||
if s1.Prand32()%2 == 0 {
|
||||
s1.icmp.Reset()
|
||||
_, err = s1.icmp.PingStart(s2.Addr().As4(), buf[:pingMinPayload], pingMinPayload+uint16(s1.Prand32())%pingMinPayload)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
} else {
|
||||
s2.icmp.Reset()
|
||||
_, err = s2.icmp.PingStart(s1.Addr().As4(), buf[:pingMinPayload], pingMinPayload+uint16(s2.Prand32())%pingMinPayload)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
}
|
||||
case actionARP:
|
||||
action := s1.Prand32() % 6
|
||||
switch action {
|
||||
case 0: // s1 queries s2 address.
|
||||
s1.StartResolveHardwareAddress6(s2.Addr())
|
||||
case 1: // s2 queries s1 address.
|
||||
s2.StartResolveHardwareAddress6(s1.Addr())
|
||||
case 2: // s1 checks query result for s2.
|
||||
s1.ResultResolveHardwareAddress6(s2.Addr())
|
||||
case 3: // s2 checks query result for s1.
|
||||
s2.ResultResolveHardwareAddress6(s1.Addr())
|
||||
case 4: // s1 discards pending query.
|
||||
s1.DiscardResolveHardwareAddress6(s2.Addr())
|
||||
case 5: // s2 discards pending query.
|
||||
s2.DiscardResolveHardwareAddress6(s1.Addr())
|
||||
}
|
||||
}
|
||||
// Exchange data while checking stack does not enter runaway infinite frame send loop.
|
||||
first, second := &s1, &s2
|
||||
if s1.Prand32()%2 == 0 {
|
||||
first, second = second, first
|
||||
}
|
||||
// TODO(soypat): add specialized packet mutation by detecting protocol and modifying specific packet fields.
|
||||
const maxConsecutivePackets = 6
|
||||
mut := s1.Prand32()
|
||||
f.Add(int64(1), int64(2))
|
||||
// Numbers below taken from ANU QRNG https://qrng.anu.edu.au/random-hex/
|
||||
f.Add(int64(0x5b38810084b73b78), int64(0xfbc7243ac2c4a84))
|
||||
f.Add(int64(0x78b75e43c6fb1336), int64(0x09f9c425438dd42a))
|
||||
f.Add(int64(0xf63789e3a0750ed), int64(0xd4d3df265f09358))
|
||||
f.Add(int64(0x9649343892132dc), int64(0xfd5be085171f904))
|
||||
|
||||
for k := 0; k < maxConsecutivePackets; k++ {
|
||||
n, err := first.EgressEthernet(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal(i, k, err)
|
||||
} else if n > 0 {
|
||||
if mut&1 == 1 {
|
||||
pmut.MutateEthernet(buf[:n], int64(s1.Prand32())|int64(s1.Prand32())<<32, int64(s1.Prand32())|int64(s1.Prand32())<<32)
|
||||
betsAreOff = true
|
||||
}
|
||||
err = second.IngressEthernet(buf[:n])
|
||||
if err != nil && !betsAreOff && err != lneto.ErrPacketDrop && err != lneto.ErrExhausted {
|
||||
t.Fatal(i, k, err)
|
||||
}
|
||||
mut >>= 1
|
||||
// Set to the values of the fuzz case that is crashing to enable verbose debugging logs.
|
||||
f.Fuzz(testStackSeeded)
|
||||
}
|
||||
|
||||
// fuzz test printing facilities.
|
||||
var (
|
||||
fzppr CapturePrinter
|
||||
fzpmut ltesto.PacketMut
|
||||
fzoutput = os.Stdout
|
||||
)
|
||||
|
||||
func init() {
|
||||
fzppr.Configure(fzoutput, CapturePrinterConfig{
|
||||
NamespaceWidth: 3,
|
||||
})
|
||||
}
|
||||
|
||||
const (
|
||||
printFuzz = false
|
||||
printSeed1 = 676827762285163398
|
||||
printSeed2 = 1141027023543727980
|
||||
)
|
||||
|
||||
// Debugging facility.
|
||||
func TestStackSeeded(t *testing.T) {
|
||||
testStackSeeded(t, printSeed1, printSeed2)
|
||||
}
|
||||
|
||||
func testStackSeeded(t *testing.T, seed1, seed2 int64) {
|
||||
if seed1 == 0 {
|
||||
seed1++
|
||||
}
|
||||
if seed2 == 0 {
|
||||
seed2++
|
||||
}
|
||||
verbose := printFuzz && printSeed1 == seed1 && printSeed2 == seed2
|
||||
const (
|
||||
actionUDP = iota
|
||||
actionTCP
|
||||
actionICMP
|
||||
actionARP
|
||||
actionLim
|
||||
)
|
||||
const maxActions = 32
|
||||
const maxConsecutivePackets = 6
|
||||
var actions [maxActions]struct {
|
||||
Action int64
|
||||
Rand int64
|
||||
Mutation [maxConsecutivePackets]struct {
|
||||
Seed1, Seed2 int64
|
||||
MutBits1, MutBits2 int64
|
||||
IsMut int64
|
||||
}
|
||||
}
|
||||
// Fuzz tests are supposed to be predictable and repeatable.
|
||||
// We only generate the randomness in one place and in same order of
|
||||
// rng.Int64 calls. We cannot add new calls into the for loop but we
|
||||
// can add a new for loops when we need more fields filled in.
|
||||
// Be wary of invalidating the entire fuzz corpus we have.
|
||||
{
|
||||
rng := rand.New(rand.NewPCG(uint64(seed1), uint64(seed2)))
|
||||
for i := range actions {
|
||||
// DO NOT ADD CALLS TO rng API IN HERE! Read comment above.
|
||||
actions[i].Action = rng.Int64() % actionLim
|
||||
actions[i].Rand = rng.Int64()
|
||||
for k := range maxConsecutivePackets {
|
||||
mut := &actions[i].Mutation[k]
|
||||
mut.IsMut = rng.Int64()
|
||||
mut.Seed1 = rng.Int64()
|
||||
mut.Seed2 = rng.Int64()
|
||||
mut.MutBits1 = rng.Int64()
|
||||
mut.MutBits2 = rng.Int64()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mtu = 1500
|
||||
const mfl = mtu + 14 // frame length includes ethernet header
|
||||
var buf [mfl]byte
|
||||
var s1, s2 StackAsync
|
||||
v1, v2 := byte(seed1), byte(seed2)
|
||||
cfg1 := StackConfig{
|
||||
Hostname: "s1",
|
||||
StaticAddress: netip.AddrFrom4([4]byte{1, 0, 0, v1}),
|
||||
RandSeed: seed1,
|
||||
MaxActiveTCPPorts: 1,
|
||||
MaxActiveUDPPorts: 1,
|
||||
ICMPQueueLimit: 1 + int(v1%4),
|
||||
MTU: mtu,
|
||||
HardwareAddress: [6]byte{0x1, 0, 0, 0, 0, v1},
|
||||
AcceptMulticast: v1%2 == 0,
|
||||
}
|
||||
err := s1.Reset(cfg1)
|
||||
if err != nil {
|
||||
t.Fatal(err, cfg1)
|
||||
}
|
||||
cfg2 := StackConfig{
|
||||
Hostname: "s2",
|
||||
StaticAddress: netip.AddrFrom4([4]byte{1, 0, 0, v2}),
|
||||
RandSeed: seed2,
|
||||
MaxActiveTCPPorts: 1,
|
||||
MaxActiveUDPPorts: 1,
|
||||
ICMPQueueLimit: 1 + int(v2%4),
|
||||
MTU: mtu,
|
||||
HardwareAddress: [6]byte{0x2, 0, 0, 0, 0, v2},
|
||||
AcceptMulticast: v2%2 == 0,
|
||||
}
|
||||
err = s2.Reset(cfg2)
|
||||
if err != nil {
|
||||
t.Fatal(err, cfg2)
|
||||
}
|
||||
|
||||
const (
|
||||
pingMinPayload = 8
|
||||
port1 = 8080
|
||||
port2 = 80
|
||||
bufsize = 64
|
||||
)
|
||||
var udp1, udp2 udp.Conn
|
||||
var tcp1, tcp2 tcp.Conn
|
||||
err = tcp1.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
TxPacketQueueSize: 1 + int(uint16(seed1)%10),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = tcp2.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
TxPacketQueueSize: 1 + int(uint16(seed2)%10),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = udp1.Configure(udp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
RxQueueSize: int(1 + uint16(seed1>>32)%10),
|
||||
TxQueueSize: int(1 + uint16(seed1>>32)%10),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = udp2.Configure(udp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
TxBuf: make([]byte, bufsize),
|
||||
RxQueueSize: int(1 + uint16(seed2>>32)%10),
|
||||
TxQueueSize: int(1 + uint16(seed2>>32)%10),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
icmpEnabled := false
|
||||
udpOrder := 0
|
||||
betsAreOff := false // When a packet is mutated all bets on which error can be returned are off.
|
||||
for i, action := range actions {
|
||||
switch action.Action {
|
||||
case actionTCP:
|
||||
state1 := tcp1.State()
|
||||
state2 := tcp2.State()
|
||||
if state1 == 0 && state2 == 0 {
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "TCP dial")
|
||||
}
|
||||
n, err = second.EgressEthernet(buf[:])
|
||||
err = s1.DialTCP(&tcp1, port1, netip.AddrPortFrom(s2.Addr(), port2))
|
||||
if err != nil {
|
||||
t.Fatal(i, k, err)
|
||||
} else if n > 0 {
|
||||
if mut&1 == 1 {
|
||||
pmut.MutateEthernet(buf[:n], int64(s1.Prand32())|int64(s1.Prand32())<<32, int64(s1.Prand32())|int64(s1.Prand32())<<32)
|
||||
betsAreOff = true
|
||||
}
|
||||
mut >>= 1
|
||||
err = first.IngressEthernet(buf[:n])
|
||||
if err != nil && !betsAreOff && err != lneto.ErrPacketDrop && err != lneto.ErrExhausted {
|
||||
t.Fatal(i, k, err)
|
||||
}
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
err = s2.ListenTCP(&tcp2, port2)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
} else if state1 == tcp.StateEstablished && state2 == tcp.StateEstablished {
|
||||
// For now just close after established.
|
||||
closeNum := 1 + action.Rand%2
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "TCP close", closeNum)
|
||||
}
|
||||
switch closeNum {
|
||||
case 1:
|
||||
tcp1.Close()
|
||||
case 2:
|
||||
tcp2.Close()
|
||||
}
|
||||
}
|
||||
case actionUDP:
|
||||
// Ensure connections open.
|
||||
if !udp1.IsOpen() {
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "UDP dial 1")
|
||||
}
|
||||
err = s1.DialUDP(&udp1, port1, netip.AddrPortFrom(s2.Addr(), port2))
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
}
|
||||
if !udp2.IsOpen() {
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "UDP dial 2")
|
||||
}
|
||||
err = s2.DialUDP(&udp2, port2, netip.AddrPortFrom(s1.Addr(), port1))
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
}
|
||||
udpOrder++
|
||||
action := action.Rand % 8
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "UDP action", action)
|
||||
}
|
||||
switch action {
|
||||
case 0:
|
||||
if udp1.FreeOutput() > 0 {
|
||||
udp1.Write([]byte{byte(udpOrder)})
|
||||
}
|
||||
case 1:
|
||||
if udp1.BufferedInput() > 0 {
|
||||
udp1.Read(buf[:])
|
||||
}
|
||||
case 2:
|
||||
if udp2.FreeOutput() > 0 {
|
||||
udp2.Write([]byte{byte(udpOrder)})
|
||||
}
|
||||
case 3:
|
||||
if udp2.BufferedInput() > 0 {
|
||||
udp2.Read(buf[:])
|
||||
}
|
||||
case 4:
|
||||
udp1.Close()
|
||||
case 5:
|
||||
udp2.Close()
|
||||
}
|
||||
case actionICMP:
|
||||
icmpaction := action.Rand % 2
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "ICMP action", icmpaction, "enabled", icmpEnabled)
|
||||
}
|
||||
if !icmpEnabled {
|
||||
err = s1.EnableICMP(true)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
err = s2.EnableICMP(true)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
icmpEnabled = true
|
||||
}
|
||||
switch icmpaction {
|
||||
case 0:
|
||||
s1.icmp.Reset()
|
||||
_, err = s1.icmp.PingStart(s2.Addr().As4(), buf[:pingMinPayload], pingMinPayload+uint16(action.Rand)%pingMinPayload)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
case 1:
|
||||
s2.icmp.Reset()
|
||||
_, err = s2.icmp.PingStart(s1.Addr().As4(), buf[:pingMinPayload], pingMinPayload+uint16(action.Rand)%pingMinPayload)
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
}
|
||||
|
||||
case actionARP:
|
||||
action := action.Rand % 6
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "ARP action", action)
|
||||
}
|
||||
switch action {
|
||||
case 0: // s1 queries s2 address.
|
||||
s1.StartResolveHardwareAddress6(s2.Addr())
|
||||
case 1: // s2 queries s1 address.
|
||||
s2.StartResolveHardwareAddress6(s1.Addr())
|
||||
case 2: // s1 checks query result for s2.
|
||||
s1.ResultResolveHardwareAddress6(s2.Addr())
|
||||
case 3: // s2 checks query result for s1.
|
||||
s2.ResultResolveHardwareAddress6(s1.Addr())
|
||||
case 4: // s1 discards pending query.
|
||||
s1.DiscardResolveHardwareAddress6(s2.Addr())
|
||||
case 5: // s2 discards pending query.
|
||||
s2.DiscardResolveHardwareAddress6(s1.Addr())
|
||||
}
|
||||
}
|
||||
// Exchange data while checking stack does not enter runaway infinite frame send loop.
|
||||
first, second := &s1, &s2
|
||||
if (action.Rand>>32)%2 == 0 {
|
||||
first, second = second, first
|
||||
}
|
||||
// TODO(soypat): add specialized packet mutation by detecting protocol and modifying specific packet fields.
|
||||
|
||||
for k, mut := range action.Mutation {
|
||||
n, err := first.EgressEthernet(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal(i, "expected no errors after maxconsecutive", err)
|
||||
t.Fatal(i, k, err)
|
||||
} else if n > 0 {
|
||||
t.Fatal(i, "expected no more data after max consecutive")
|
||||
if mut.IsMut&1 != 0 {
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "mutate tx", first.Hostname())
|
||||
}
|
||||
fzpmut.MutateEthernet(buf[:n], mut.Seed1, mut.MutBits1)
|
||||
betsAreOff = true
|
||||
}
|
||||
if verbose {
|
||||
fzppr.PrintPacket(first.Hostname(), buf[:n])
|
||||
}
|
||||
err = second.IngressEthernet(buf[:n])
|
||||
if err != nil && !betsAreOff && err != lneto.ErrPacketDrop && err != lneto.ErrExhausted {
|
||||
t.Fatal(i, k, err)
|
||||
} else if verbose && err != nil {
|
||||
fmt.Fprintln(fzoutput, "err rx", second.Hostname(), err.Error())
|
||||
}
|
||||
}
|
||||
n, err = second.EgressEthernet(buf[:])
|
||||
if err != nil {
|
||||
t.Fatal(i, "expected no errors after maxconsecutive", err)
|
||||
t.Fatal(i, k, err)
|
||||
} else if n > 0 {
|
||||
t.Fatal(i, "expected no more data after max consecutive")
|
||||
if mut.IsMut&1 != 0 {
|
||||
if verbose {
|
||||
fmt.Fprintln(fzoutput, "mutate tx", second.Hostname())
|
||||
}
|
||||
fzpmut.MutateEthernet(buf[:n], mut.Seed2, mut.MutBits2)
|
||||
betsAreOff = true
|
||||
}
|
||||
if verbose {
|
||||
fzppr.PrintPacket(second.Hostname(), buf[:n])
|
||||
}
|
||||
err = first.IngressEthernet(buf[:n])
|
||||
if err != nil && !betsAreOff && err != lneto.ErrPacketDrop && err != lneto.ErrExhausted {
|
||||
t.Fatal(i, k, err)
|
||||
} else if verbose && err != nil {
|
||||
fmt.Fprintln(fzoutput, "err rx", first.Hostname(), err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// Drain any remaining packets (retransmits from mutation).
|
||||
// Hard ceiling prevents infinite send loops from passing silently.
|
||||
// Also send drained packet to other stack to also catch infinite feedback loops.
|
||||
const drainLimit = 8
|
||||
for d := 0; d < drainLimit; d++ {
|
||||
limit := d == drainLimit-1
|
||||
n, err := first.EgressEthernet(buf[:])
|
||||
if (err != nil || n > 0) && limit {
|
||||
fzppr.PrintPacket(first.Hostname(), buf[:n])
|
||||
t.Fatal(i, "stuck in data/error loop:", err)
|
||||
} else if n > 0 {
|
||||
if verbose {
|
||||
fzppr.PrintPacket(first.Hostname(), buf[:n])
|
||||
}
|
||||
second.IngressEthernet(buf[:n])
|
||||
}
|
||||
n, err = second.EgressEthernet(buf[:])
|
||||
if (err != nil || n > 0) && limit {
|
||||
fzppr.PrintPacket("(2) ", buf[:n])
|
||||
t.Fatal(i, "stuck in data/error loop:", err)
|
||||
} else if n > 0 {
|
||||
if verbose {
|
||||
fzppr.PrintPacket(second.Hostname(), buf[:n])
|
||||
}
|
||||
first.IngressEthernet(buf[:n])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user