diff --git a/internet/basicstack.go b/internet/basicstack.go index 865b01d..197661e 100644 --- a/internet/basicstack.go +++ b/internet/basicstack.go @@ -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()) diff --git a/internet/stackbasic_test.go b/internet/stackbasic_test.go index 2ae839f..de9175a 100644 --- a/internet/stackbasic_test.go +++ b/internet/stackbasic_test.go @@ -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) } } diff --git a/ipv4/definitions.go b/ipv4/definitions.go index 14c45ee..4097e58 100644 --- a/ipv4/definitions.go +++ b/ipv4/definitions.go @@ -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]. diff --git a/ipv4/frame_test.go b/ipv4/frame_test.go new file mode 100644 index 0000000..88f1d25 --- /dev/null +++ b/ipv4/frame_test.go @@ -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) + } + } +}