add ipv4 frame tests; fix protocol setting

This commit is contained in:
soypat
2025-05-13 21:36:24 -03:00
parent 4b3db8fefd
commit 411d1b3c28
4 changed files with 125 additions and 7 deletions
+3 -1
View File
@@ -61,7 +61,8 @@ func (sb *StackBasic) Recv(frame []byte) error {
totalLen := ifrm.TotalLength()
for i := range sb.handlers {
h := &sb.handlers[i]
if h.proto == ifrm.Protocol() {
proto := ifrm.Protocol()
if h.proto == proto {
return h.recv(frame[:totalLen], off)
}
}
@@ -92,6 +93,7 @@ func (sb *StackBasic) Handle(frame []byte) (int, error) {
ifrm.SetTotalLength(uint16(totalLen))
ifrm.SetFlags(dontFrag)
ifrm.SetTTL(64)
ifrm.SetProtocol(h.proto)
ifrm.SetCRC(ifrm.CalculateHeaderCRC())
if ifrm.Protocol() == lneto.IPProtoTCP {
tfrm, _ := tcp.NewFrame(ifrm.Payload())
+11 -4
View File
@@ -14,9 +14,11 @@ func TestBasicStack(t *testing.T) {
var connCl, connSv TCPConn
setupClientServer(t, rng, &sbCl, &sbSv, &connCl, &connSv)
var buf [2048]byte
nextToSend := &sbCl
nextToRecv := &sbSv
exchangeAndExpectStates := func(clState, svState tcp.State) {
t.Helper()
expectExchange(t, &sbCl, &sbSv, buf[:])
expectExchange(t, nextToSend, nextToRecv, buf[:])
gotCl := connCl.State()
gotSv := connSv.State()
if gotCl != clState {
@@ -25,20 +27,25 @@ func TestBasicStack(t *testing.T) {
if gotSv != svState {
t.Errorf("want server state %s, got %s", svState, gotSv)
}
nextToSend, nextToRecv = nextToRecv, nextToSend
}
exchangeAndExpectStates(tcp.StateSynSent, tcp.StateListen)
exchangeAndExpectStates(tcp.StateSynSent, tcp.StateSynRcvd) // Client sends over first SYN and server receives it.
exchangeAndExpectStates(tcp.StateEstablished, tcp.StateSynRcvd) // server sends back SYNACK, establishing connection on client side.
exchangeAndExpectStates(tcp.StateEstablished, tcp.StateEstablished) // Client sends ACK, establishing connection in full.
}
func expectExchange(t *testing.T, from, to *StackBasic, buf []byte) {
t.Helper()
n, err := from.Handle(buf)
if err != nil {
t.Error(err)
t.Error("expectExchange:Handle:", err)
} else if n == 0 {
t.Error("expected data exchange")
return
}
err = to.Recv(buf[:n])
if err != nil {
t.Error(err)
t.Error("expectExchange:Recv:", err)
}
}
+2 -2
View File
@@ -4,7 +4,7 @@ const (
sizeHeader = 20
)
// ToS represents the Traffic Class (a.k.a Type of Service).
// ToS represents the Traffic Class (a.k.a Type of Service). It is 8 bits long. 6 MSB are Differentiated Services; 2 LSB are Explicit Congenstion Notification.
type ToS uint8
// DS returns the top 6 bits of the IPv4 ToS holding the Differentiated Services field
@@ -14,7 +14,7 @@ func (tos ToS) DS() uint8 { return uint8(tos) >> 2 }
// ECN is the Explicit Congestion Notification which provides congestion control and non-congestion control traffic.
func (tos ToS) ECN() uint8 { return uint8(tos & 0b11) }
// Flags holds fragmentation field data of an IPv4 header.
// Flags holds fragmentation field data of an IPv4 header. It is 16 bits long.
type Flags uint16
// IsEvil returns true if evil bit set as per [RFC3514].
+109
View File
@@ -0,0 +1,109 @@
package ipv4
import (
"math"
"math/rand"
"testing"
"github.com/soypat/lneto"
)
func TestFrame(t *testing.T) {
var buf [1024]byte
ifrm, err := NewFrame(buf[:])
if err != nil {
t.Fatal(err)
}
rng := rand.New(rand.NewSource(1))
const wantVersion = 4
v := new(lneto.Validator)
for i := 0; i < 100; i++ {
// SET VALUES:
wantIHL := uint8(5 + rng.Intn(10))
wantToS := ToS(rng.Intn(4))
ifrm.SetVersionAndIHL(wantVersion, wantIHL)
wantPayloadLen := rng.Intn(6)
ifrm.SetToS(wantToS)
wantTotalLength := 4*uint16(wantIHL) + uint16(wantPayloadLen)
ifrm.SetTotalLength(wantTotalLength)
wantID := uint16(rng.Intn(math.MaxUint16))
ifrm.SetID(wantID)
wantFlags := Flags(rng.Intn(16))
ifrm.SetFlags(wantFlags)
wantTTL := uint8(rng.Intn(256))
ifrm.SetTTL(wantTTL)
wantProtocol := lneto.IPProto(rng.Intn(256))
ifrm.SetProtocol(wantProtocol)
wantCRC := uint16(rng.Intn(math.MaxUint16))
ifrm.SetCRC(wantCRC)
src := ifrm.SourceAddr()
rng.Read(src[:])
wantSrc := *src
dst := ifrm.DestinationAddr()
rng.Read(dst[:])
wantDst := *dst
ifrm.ValidateExceptCRC(v)
ifrm.ValidateSize(v)
if v.Err() != nil {
t.Error(v.Err())
}
// OPTION+PAYLOAD VALIDATION:
opts := ifrm.Options()
payload := ifrm.Payload()
payloadOff := int(wantIHL) * 4
wantOptions := buf[sizeHeader:payloadOff]
wantPayload := buf[payloadOff : payloadOff+wantPayloadLen]
if len(payload) != wantPayloadLen {
t.Errorf("want payload length %d, got %d", wantPayloadLen, len(payload))
}
if len(opts) != len(wantOptions) {
t.Errorf("want length of options %d, got %d", len(wantOptions), len(opts))
}
if len(opts) > 0 && &wantOptions[0] != &opts[0] {
t.Error("first byte of options unexpected pointer")
}
if len(payload) > 0 && &wantPayload[0] != &payload[0] {
t.Error("first byte of payload unexpected pointer")
}
if len(payload) > 0 {
payload[0] = byte(rng.Int()) // write over start of payload to catch field aliasing.
}
if len(opts) > 0 {
opts[0] = byte(rng.Int()) // Catch field aliasing.
}
// FIELD VALIDATION:
if ver, ihl := ifrm.VersionAndIHL(); ver != wantVersion || ihl != wantIHL {
t.Errorf("wanted IHL %d, got version,IHL %d,%d ", wantIHL, ver, ihl)
}
if tos := ifrm.ToS(); tos != wantToS {
t.Errorf("wanted ToS %d, got %d", wantToS, tos)
}
if tl := ifrm.TotalLength(); tl != wantTotalLength {
t.Errorf("wanted total length %d, got %d", wantTotalLength, tl)
}
if id := ifrm.ID(); id != wantID {
t.Errorf("want ID %d, got %d", wantID, id)
}
if flags := ifrm.Flags(); flags != wantFlags {
t.Errorf("want flags %d, got %d", wantFlags, flags)
}
if ttl := ifrm.TTL(); ttl != wantTTL {
t.Errorf("want TTL %d, got %d", wantTTL, ttl)
}
if proto := ifrm.Protocol(); proto != wantProtocol {
t.Errorf("want protocol %d, got %d", wantProtocol, proto)
}
if crc := ifrm.CRC(); crc != wantCRC {
t.Errorf("want crc %d, got %d", wantCRC, crc)
}
if wantDst != *dst {
t.Errorf("want dst addr %d, got %d", wantDst, dst)
}
if wantSrc != *src {
t.Errorf("want src addr %d, got %d", wantSrc, src)
}
}
}