mirror of
https://github.com/soypat/lneto.git
synced 2026-09-10 08:39:30 +00:00
fix tests not passing for RingTx
This commit is contained in:
+17
-9
@@ -1,11 +1,13 @@
|
|||||||
package lneto
|
package lneto_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/soypat/lneto"
|
||||||
"github.com/soypat/lneto/internal/ltesto"
|
"github.com/soypat/lneto/internal/ltesto"
|
||||||
|
"github.com/soypat/lneto/tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTCPMarshalUnmarshal(t *testing.T) {
|
func TestTCPMarshalUnmarshal(t *testing.T) {
|
||||||
@@ -16,7 +18,13 @@ func TestTCPMarshalUnmarshal(t *testing.T) {
|
|||||||
src := make([]byte, maxSize)
|
src := make([]byte, maxSize)
|
||||||
dst := make([]byte, maxSize)
|
dst := make([]byte, maxSize)
|
||||||
for i := 0; i < 512; i++ {
|
for i := 0; i < 512; i++ {
|
||||||
src = gen.AppendRandomIPv4TCPPacket(src[:0], rng)
|
src = gen.AppendRandomIPv4TCPPacket(src[:0], rng, tcp.Segment{
|
||||||
|
SEQ: tcp.Value(rng.Int()),
|
||||||
|
ACK: tcp.Value(rng.Int()),
|
||||||
|
DATALEN: tcp.Size(rng.Intn(256)),
|
||||||
|
WND: tcp.Size(rng.Intn(1024)),
|
||||||
|
Flags: tcp.FlagACK,
|
||||||
|
})
|
||||||
dst = dst[:len(src)]
|
dst = dst[:len(src)]
|
||||||
testMoveTCPPacket(t, src, dst)
|
testMoveTCPPacket(t, src, dst)
|
||||||
if !bytes.Equal(src, dst) {
|
if !bytes.Equal(src, dst) {
|
||||||
@@ -29,31 +37,31 @@ func testMoveTCPPacket(t *testing.T, src, dst []byte) {
|
|||||||
if len(src) != len(dst) {
|
if len(src) != len(dst) {
|
||||||
panic("expect src and dst same length")
|
panic("expect src and dst same length")
|
||||||
}
|
}
|
||||||
efrm, err := NewEthFrame(src)
|
efrm, err := lneto.NewEthFrame(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
epl := efrm.Payload()
|
epl := efrm.Payload()
|
||||||
ifrm, err := NewIPv4Frame(epl)
|
ifrm, err := lneto.NewIPv4Frame(epl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ipl := ifrm.Payload()
|
ipl := ifrm.Payload()
|
||||||
tfrm, err := NewTCPFrame(ipl)
|
tfrm, err := lneto.NewTCPFrame(ipl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
efrm2, _ := NewEthFrame(dst)
|
efrm2, _ := lneto.NewEthFrame(dst)
|
||||||
*efrm2.DestinationHardwareAddr() = *efrm.DestinationHardwareAddr()
|
*efrm2.DestinationHardwareAddr() = *efrm.DestinationHardwareAddr()
|
||||||
*efrm2.SourceHardwareAddr() = *efrm.SourceHardwareAddr()
|
*efrm2.SourceHardwareAddr() = *efrm.SourceHardwareAddr()
|
||||||
efrm2.SetEtherType(efrm.EtherTypeOrSize())
|
efrm2.SetEtherType(efrm.EtherTypeOrSize())
|
||||||
if efrm.EtherTypeOrSize() == EtherTypeVLAN {
|
if efrm.EtherTypeOrSize() == lneto.EtherTypeVLAN {
|
||||||
efrm2.SetVLANTag(efrm.VLANTag())
|
efrm2.SetVLANTag(efrm.VLANTag())
|
||||||
efrm2.SetVLANEtherType(efrm.VLANEtherType())
|
efrm2.SetVLANEtherType(efrm.VLANEtherType())
|
||||||
}
|
}
|
||||||
|
|
||||||
ifrm2, _ := NewIPv4Frame(efrm2.Payload())
|
ifrm2, _ := lneto.NewIPv4Frame(efrm2.Payload())
|
||||||
ifrm2.SetVersionAndIHL(ifrm.VersionAndIHL())
|
ifrm2.SetVersionAndIHL(ifrm.VersionAndIHL())
|
||||||
ifrm2.SetToS(ifrm.ToS())
|
ifrm2.SetToS(ifrm.ToS())
|
||||||
ifrm2.SetFlags(ifrm.Flags())
|
ifrm2.SetFlags(ifrm.Flags())
|
||||||
@@ -65,7 +73,7 @@ func testMoveTCPPacket(t *testing.T, src, dst []byte) {
|
|||||||
*ifrm2.SourceAddr() = *ifrm.SourceAddr()
|
*ifrm2.SourceAddr() = *ifrm.SourceAddr()
|
||||||
*ifrm2.DestinationAddr() = *ifrm.DestinationAddr()
|
*ifrm2.DestinationAddr() = *ifrm.DestinationAddr()
|
||||||
|
|
||||||
tfrm2, _ := NewTCPFrame(ifrm2.Payload())
|
tfrm2, _ := lneto.NewTCPFrame(ifrm2.Payload())
|
||||||
tfrm2.SetSourcePort(tfrm.SourcePort())
|
tfrm2.SetSourcePort(tfrm.SourcePort())
|
||||||
tfrm2.SetDestinationPort(tfrm.DestinationPort())
|
tfrm2.SetDestinationPort(tfrm.DestinationPort())
|
||||||
tfrm2.SetSeq(tfrm.Seq())
|
tfrm2.SetSeq(tfrm.Seq())
|
||||||
|
|||||||
+101
-56
@@ -7,15 +7,10 @@ import (
|
|||||||
"github.com/soypat/lneto/internal"
|
"github.com/soypat/lneto/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRingTx(buf []byte, maxQueuedPackets int) *ringTx {
|
const (
|
||||||
if maxQueuedPackets <= 0 || len(buf) < 2 || len(buf) < maxQueuedPackets {
|
// this must be at least 2 for buffer to work.
|
||||||
panic("invalid argument to NewRingTx")
|
minBufferSize = 2
|
||||||
}
|
)
|
||||||
return &ringTx{
|
|
||||||
rawbuf: buf,
|
|
||||||
packets: make([]ringidx, maxQueuedPackets),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ringTx is a ring buffer with retransmission queue functionality added.
|
// ringTx is a ring buffer with retransmission queue functionality added.
|
||||||
type ringTx struct {
|
type ringTx struct {
|
||||||
@@ -24,12 +19,15 @@ type ringTx struct {
|
|||||||
// packets contains
|
// packets contains
|
||||||
packets []ringidx
|
packets []ringidx
|
||||||
// _firstPkt is the index of the oldest packet in the packets field.
|
// _firstPkt is the index of the oldest packet in the packets field.
|
||||||
_firstPkt int
|
// _firstPkt int
|
||||||
_lastPkt int
|
// _lastPkt int
|
||||||
// unsentOff is the offset of start of unsent data into rawbuf.
|
// unsentOff is the offset of start of unsent data into rawbuf.
|
||||||
unsentoff int
|
unsentoff int
|
||||||
// unsentend is the offset of end of unsent data in rawbuf.
|
// unsentend is the offset of end of unsent data in rawbuf.
|
||||||
unsentend int
|
unsentend int
|
||||||
|
seq Value
|
||||||
|
// always empty ring.
|
||||||
|
emptyRing ringidx
|
||||||
}
|
}
|
||||||
|
|
||||||
// ringidx represents packet data inside RingTx
|
// ringidx represents packet data inside RingTx
|
||||||
@@ -45,6 +43,40 @@ type ringidx struct {
|
|||||||
// acked bool
|
// acked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset resets the RingTx's internal state to use buf as the main ring buffer and creates or reuses
|
||||||
|
// the packet ring buffer.
|
||||||
|
func (rx *ringTx) Reset(buf []byte, maxqueuedPackets int, seq Value) error {
|
||||||
|
if maxqueuedPackets <= 0 {
|
||||||
|
return errors.New("queued packets <=0")
|
||||||
|
} else if len(buf) < minBufferSize || len(buf) < maxqueuedPackets {
|
||||||
|
return errors.New("invalid buffer size")
|
||||||
|
}
|
||||||
|
if cap(rx.packets) < maxqueuedPackets {
|
||||||
|
rx.packets = make([]ringidx, maxqueuedPackets)
|
||||||
|
}
|
||||||
|
*rx = ringTx{
|
||||||
|
rawbuf: buf,
|
||||||
|
packets: rx.packets[:maxqueuedPackets],
|
||||||
|
seq: seq,
|
||||||
|
}
|
||||||
|
for i := range rx.packets {
|
||||||
|
rx.packets[i].markRcvd()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetOrReuse is identical to a call to [ringTx.Reset] with the additional detail that
|
||||||
|
// the zero value of buf (nil) and maxQueuedPackets (0) will selectively reuse existing data buffer and/or packet index buffer.
|
||||||
|
func (rx *ringTx) ResetOrReuse(buf []byte, maxQueuedPackets int, ack Value) error {
|
||||||
|
if buf == nil {
|
||||||
|
buf = rx.rawbuf
|
||||||
|
}
|
||||||
|
if maxQueuedPackets == 0 {
|
||||||
|
maxQueuedPackets = len(rx.packets)
|
||||||
|
}
|
||||||
|
return rx.Reset(buf, maxQueuedPackets, ack)
|
||||||
|
}
|
||||||
|
|
||||||
// Buffered returns the amount of unsent bytes.
|
// Buffered returns the amount of unsent bytes.
|
||||||
func (tx *ringTx) Buffered() int {
|
func (tx *ringTx) Buffered() int {
|
||||||
r := tx.unsentRing()
|
r := tx.unsentRing()
|
||||||
@@ -59,9 +91,9 @@ func (tx *ringTx) BufferedSent() int {
|
|||||||
|
|
||||||
// Write writes data to the underlying unsent data ring buffer.
|
// Write writes data to the underlying unsent data ring buffer.
|
||||||
func (tx *ringTx) Write(b []byte) (n int, err error) {
|
func (tx *ringTx) Write(b []byte) (n int, err error) {
|
||||||
first := tx.packets[tx._firstPkt]
|
first := tx.pkt(tx.firstPkt())
|
||||||
r := tx.unsentRing()
|
r := tx.unsentRing()
|
||||||
if first.off < 0 {
|
if !first.sent() {
|
||||||
// No packets in queue case.
|
// No packets in queue case.
|
||||||
n, err = r.Write(b)
|
n, err = r.Write(b)
|
||||||
} else {
|
} else {
|
||||||
@@ -76,32 +108,26 @@ func (tx *ringTx) Write(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// MakePacket reads from the unsent data ring buffer and generates a new packet segment.
|
// MakePacket reads from the unsent data ring buffer and generates a new packet segment.
|
||||||
// It fails if the sent packet queue is full.
|
// It fails if the sent packet queue is full.
|
||||||
func (tx *ringTx) MakePacket(b []byte) (int, error) {
|
func (tx *ringTx) MakePacket(b []byte) (int, Value, error) {
|
||||||
nxtpkt := (tx._lastPkt + 1) % len(tx.packets)
|
nxtpkt := tx.nextPkt()
|
||||||
if tx._firstPkt == nxtpkt {
|
if tx.nextPkt() < 0 {
|
||||||
return 0, errors.New("packet queue full")
|
return 0, 0, errors.New("queue full")
|
||||||
}
|
}
|
||||||
|
|
||||||
r := tx.unsentRing()
|
r := tx.unsentRing()
|
||||||
start := r.Off
|
start := r.Off
|
||||||
n, err := r.Read(b)
|
n, err := r.Read(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, 0, err
|
||||||
}
|
}
|
||||||
last := &tx.packets[tx._lastPkt]
|
plen := Value(n)
|
||||||
rlast := tx.packetRing(tx._lastPkt)
|
seq := tx.seq
|
||||||
tx.packets[nxtpkt].off = start
|
tx.packets[nxtpkt].off = start
|
||||||
tx.packets[nxtpkt].end = tx.addOff(start, n)
|
tx.packets[nxtpkt].end = tx.addOff(start, n)
|
||||||
tx.packets[nxtpkt].seq = last.seq + Value(rlast.Buffered())
|
tx.packets[nxtpkt].seq = seq + plen
|
||||||
tx._lastPkt = nxtpkt
|
|
||||||
tx.unsentoff = tx.addOff(tx.unsentoff, n)
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsQueueFull returns true if the sent packet queue is full in which
|
tx.unsentoff = tx.addOff(tx.unsentoff, n)
|
||||||
// case a call to ReadPacket is guaranteed to fail.
|
tx.seq += plen
|
||||||
func (tx *ringTx) IsQueueFull() bool {
|
return n, seq, nil
|
||||||
return tx._firstPkt == (tx._lastPkt+1)%len(tx.packets)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *ringTx) packetRing(i int) internal.Ring {
|
func (tx *ringTx) packetRing(i int) internal.Ring {
|
||||||
@@ -114,41 +140,29 @@ func (tx *ringTx) packetRing(i int) internal.Ring {
|
|||||||
|
|
||||||
// RecvSegment processes an incoming segment and updates the sent packet queue
|
// RecvSegment processes an incoming segment and updates the sent packet queue
|
||||||
func (tx *ringTx) RecvACK(ack Value) error {
|
func (tx *ringTx) RecvACK(ack Value) error {
|
||||||
i := tx._firstPkt
|
for i := range tx.packets {
|
||||||
for {
|
|
||||||
pkt := &tx.packets[i]
|
pkt := &tx.packets[i]
|
||||||
if ack >= pkt.seq {
|
if pkt.sent() && pkt.seq.LessThanEq(ack) {
|
||||||
// Packet was received by remote. Mark it as acked.
|
pkt.markRcvd()
|
||||||
pkt.off = -1
|
|
||||||
tx._firstPkt++
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if i == tx._lastPkt {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i = (i + 1) % len(tx.packets)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *ringTx) unsentRing() internal.Ring {
|
func (tx *ringTx) unsentRing() internal.Ring {
|
||||||
return tx.ring(tx.unsentoff, tx.unsentend)
|
off := tx.unsentoff
|
||||||
}
|
if off == tx.unsentend && off != 0 {
|
||||||
|
off--
|
||||||
func (tx *ringTx) freeRing() (internal.Ring, int) {
|
}
|
||||||
return tx.ring(tx.unsentoff, tx.unsentend), 0
|
return tx.ring(off, tx.unsentend)
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *ringTx) a() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *ringTx) sentRing() internal.Ring {
|
func (tx *ringTx) sentRing() internal.Ring {
|
||||||
first := tx.packets[tx._firstPkt]
|
first := tx.pkt(tx.firstPkt())
|
||||||
if first.off < 0 {
|
if !first.sent() {
|
||||||
return tx.ring(0, 0)
|
return internal.Ring{}
|
||||||
}
|
}
|
||||||
last := tx.packets[tx._lastPkt]
|
last := tx.pkt(tx.lastPkt())
|
||||||
return tx.ring(first.off, last.end)
|
return tx.ring(first.off, last.end)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,12 +179,21 @@ func (tx *ringTx) addOff(a, b int) int {
|
|||||||
return off
|
return off
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *ringTx) pkt(i int) *ringidx {
|
||||||
|
if i == -1 {
|
||||||
|
return &tx.emptyRing
|
||||||
|
} else if i < 0 || i >= len(tx.packets) {
|
||||||
|
panic("invalid packet index")
|
||||||
|
}
|
||||||
|
return &tx.packets[i]
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *ringTx) firstPkt() int {
|
func (tx *ringTx) firstPkt() int {
|
||||||
seq := tx.packets[0].seq
|
seq := tx.packets[0].seq
|
||||||
idx := -1
|
idx := -1
|
||||||
for i := 0; i < len(tx.packets); i++ {
|
for i := 0; i < len(tx.packets); i++ {
|
||||||
pkt := &tx.packets[i]
|
pkt := &tx.packets[i]
|
||||||
if (pkt.end != 0 || pkt.off != 0) && seq.LessThanEq(pkt.seq) {
|
if pkt.sent() && seq.LessThanEq(pkt.seq) {
|
||||||
seq = pkt.seq
|
seq = pkt.seq
|
||||||
idx = i
|
idx = i
|
||||||
}
|
}
|
||||||
@@ -183,10 +206,32 @@ func (tx *ringTx) lastPkt() int {
|
|||||||
idx := -1
|
idx := -1
|
||||||
for i := 0; i < len(tx.packets); i++ {
|
for i := 0; i < len(tx.packets); i++ {
|
||||||
pkt := &tx.packets[i]
|
pkt := &tx.packets[i]
|
||||||
if (pkt.end != 0 || pkt.off != 0) && pkt.seq.LessThanEq(seq) {
|
if pkt.sent() && pkt.seq.LessThanEq(seq) {
|
||||||
seq = pkt.seq
|
seq = pkt.seq
|
||||||
idx = i
|
idx = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *ringTx) nextPkt() int {
|
||||||
|
idx := -1
|
||||||
|
for i := 0; i < len(tx.packets); i++ {
|
||||||
|
pkt := &tx.packets[i]
|
||||||
|
if !pkt.sent() {
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *ringidx) sent() bool {
|
||||||
|
return pkt.end != 0 || pkt.off != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *ringidx) markRcvd() {
|
||||||
|
*pkt = ringidx{}
|
||||||
|
// pkt.end = 0
|
||||||
|
// pkt.off = 0
|
||||||
|
}
|
||||||
|
|||||||
+37
-17
@@ -5,32 +5,52 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTxQueueWrite(t *testing.T) {
|
func TestTxQueue_SequentialMessages(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
bufsize = 1024
|
bufsize = 2
|
||||||
maxPkt = 3
|
maxPkt = 1
|
||||||
msg = "hello world"
|
msg = "hello world"
|
||||||
|
startAck = 0 // this is the initial sequence number.
|
||||||
)
|
)
|
||||||
buf := make([]byte, bufsize)
|
buf := make([]byte, bufsize)
|
||||||
rtx := newRingTx(buf, maxPkt)
|
var rtx ringTx
|
||||||
|
err := rtx.Reset(buf, maxPkt, startAck)
|
||||||
bufs := bytes.SplitAfter([]byte(msg), []byte("e"))
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// msgs := bytes.SplitAfter([]byte(msg), []byte("e"))
|
||||||
|
msgs := bytes.Split([]byte(msg), []byte(""))
|
||||||
var data [bufsize]byte
|
var data [bufsize]byte
|
||||||
for i, buf := range bufs {
|
prevSeq := Value(startAck)
|
||||||
n, err := rtx.Write(buf)
|
for i, msg := range msgs {
|
||||||
|
n, err := rtx.Write(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("writing packet %d: %s", i, err)
|
t.Fatalf("writing packet %d: %s", i, err)
|
||||||
} else if n != len(buf) {
|
} else if n != len(msg) {
|
||||||
t.Fatalf("want %d written, got %d", len(buf), n)
|
t.Fatalf("want %d written, got %d", len(msg), n)
|
||||||
}
|
}
|
||||||
|
unsent := rtx.Buffered()
|
||||||
n, err = rtx.MakePacket(data[:])
|
if len(msg) != unsent {
|
||||||
|
t.Fatalf("want %d unsent buffered, got %d", unsent, len(msg))
|
||||||
|
}
|
||||||
|
sent := rtx.BufferedSent()
|
||||||
|
if sent > 0 {
|
||||||
|
t.Fatalf("want 0 bytes sent, got %d", sent)
|
||||||
|
}
|
||||||
|
n, seq, err := rtx.MakePacket(data[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("making packet %d: %s", i, err)
|
t.Fatalf("making packet %d: %s", i, err)
|
||||||
} else if n != len(buf) {
|
} else if n != len(msg) {
|
||||||
t.Fatalf("want %d packet read, got %d", len(buf), n)
|
t.Fatalf("want %d packet read, got %d", len(msg), n)
|
||||||
} else if !bytes.Equal(buf, data[:n]) {
|
} else if !bytes.Equal(msg, data[:n]) {
|
||||||
t.Fatalf("want data %q, got data read %q", buf, data[:n])
|
t.Fatalf("want data %q, got data read %q", msg, data[:n])
|
||||||
|
} else if seq != prevSeq {
|
||||||
|
t.Fatalf("want seq %d, got %d", prevSeq, seq)
|
||||||
|
}
|
||||||
|
prevSeq += Value(n)
|
||||||
|
err = rtx.RecvACK(prevSeq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user