mirror of
https://github.com/soypat/lneto.git
synced 2026-08-20 14:39:02 +00:00
tcp: retransmit logic rollback (#61)
* tcp: remove retransmit logic entirely; add duplicate ack counting to ControlBlock * retransmit implemented in nice simple straightforward way * narrow down retransmission cases * remove old timing tests * add ControlBlock retransmit test * add failing handler test * reworking payload length semantic meaning in code * fix establish conn logic * clean up tests and add TCB dupack generation and test it * catch pending retransmit satisfy in test * add fuzz test for control block * bugfix: be more strict in what is considered dupack * add IncomingIsDupACK docs * limit queue of retransmits * protect retransmit overflow from incorrectly updating nxt
This commit is contained in:
+20
-41
@@ -45,9 +45,6 @@ type ringidx struct {
|
||||
seq Value
|
||||
// size is the size of the packet in bytes.
|
||||
size Size
|
||||
// sentAt is the time in milliseconds when this packet was first sent.
|
||||
// Used for RTO detection per RFC 6298 §5.
|
||||
sentAt uint32
|
||||
}
|
||||
|
||||
// Reset resets the RingTx's internal state to use buf as the main ring buffer and creates or reuses
|
||||
@@ -118,20 +115,31 @@ func (rtx *ringTx) Write(b []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
// MakePacket reads from the unsent data ring buffer and generates a new packet segment.
|
||||
// It fails if the sent packet queue is full. sentAt is the current time in milliseconds,
|
||||
// stamped on the packet for RTO detection per RFC 6298 §5.1.
|
||||
func (rtx *ringTx) MakePacket(b []byte, currentSeq Value, sentAt uint32) (int, error) {
|
||||
// It fails if the sent packet queue is full.
|
||||
func (rtx *ringTx) MakePacket(b []byte, currentSeq Value) (int, error) {
|
||||
free := rtx.slist.Free()
|
||||
if free == 0 {
|
||||
return 0, lneto.ErrBufferFull
|
||||
}
|
||||
endSeq, ok := rtx.sentEndSeq()
|
||||
if ok && currentSeq.LessThan(endSeq) {
|
||||
// maybe retransmit. Look for exact match.
|
||||
for i := range rtx.slist.pkts {
|
||||
pkt := &rtx.slist.pkts[i]
|
||||
if pkt.seq == currentSeq {
|
||||
// This packet to be retransmit.
|
||||
data := rtx.ring(pkt.off, pkt.end)
|
||||
return data.Read(b)
|
||||
}
|
||||
}
|
||||
internal.LogAttrs(nil, slog.LevelError, "txqueue:seq<endseq", slog.Uint64("seq", uint64(currentSeq)), slog.Uint64("endseq", uint64(endSeq)))
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
// Reading unsent ring consumes unsent and converts it to "sent".
|
||||
unsent, _ := rtx.unsentRing()
|
||||
if unsent.IsEmpty() {
|
||||
return 0, nil // No data to send.
|
||||
}
|
||||
oldUnsentOff := unsent.Off
|
||||
n, err := unsent.Read(b)
|
||||
if err != nil {
|
||||
@@ -141,7 +149,7 @@ func (rtx *ringTx) MakePacket(b []byte, currentSeq Value, sentAt uint32) (int, e
|
||||
// Start of buffer will be SENT, end of buffer will be UNSENT(or empty).
|
||||
// Packet generated has offset at old unsentOff.
|
||||
size := rtx.Size()
|
||||
pkt := rtx.slist.AddPacket(n, oldUnsentOff, size, currentSeq, sentAt)
|
||||
pkt := rtx.slist.AddPacket(n, oldUnsentOff, size, currentSeq)
|
||||
if pkt.off != oldUnsentOff || pkt.end != addEnd(pkt.off, n, size) {
|
||||
panic("invalid generated packet")
|
||||
}
|
||||
@@ -233,34 +241,6 @@ func (rtx *ringTx) RetransmitFromUNA() {
|
||||
rtx.slist.Reset(cap(rtx.slist.pkts), unaSeq)
|
||||
}
|
||||
|
||||
// RecoveryACK processes a cumulative ACK that covers data sent before a
|
||||
// retransmit rewind. After RetransmitFromUNA merged sent→unsent and cleared
|
||||
// the sentlist, a recovery ACK may exceed what's currently in the sentlist.
|
||||
// This method acks any sentlist entries, then skips unsent bytes that were
|
||||
// implicitly acknowledged (they were received by the remote before the rewind).
|
||||
func (rtx *ringTx) RecoveryACK(ack Value) {
|
||||
size := rtx.Size()
|
||||
// First, ack everything in the sentlist (if any packets were re-sent).
|
||||
if newest := rtx.slist.Newest(); newest != nil {
|
||||
rtx.slist.RecvAck(newest.endSeq(), size)
|
||||
}
|
||||
rtx.sentoff = 0
|
||||
rtx.sentend = 0
|
||||
|
||||
// Skip unsent data that was implicitly acked. The sequence of the first
|
||||
// unsent byte is slist.ssn (the end-seq of the last acked packet).
|
||||
excess := int32(ack - rtx.slist.ssn)
|
||||
if excess > 0 && rtx.unsentend != 0 {
|
||||
rtx.unsentoff = addOff(rtx.unsentoff, int(excess), size)
|
||||
if rtx.unsentoff == rtx.unsentend {
|
||||
rtx.unsentoff = 0
|
||||
rtx.unsentend = 0
|
||||
}
|
||||
}
|
||||
rtx.slist.Reset(cap(rtx.slist.pkts), ack)
|
||||
rtx.consolidateBufs()
|
||||
}
|
||||
|
||||
func (rtx *ringTx) consolidateBufs() {
|
||||
unsentEmpty := rtx.unsentend == 0
|
||||
sentEmpty := rtx.sentend == 0
|
||||
@@ -351,7 +331,7 @@ func (sl *sentlist) Free() int {
|
||||
return cap(sl.pkts) - len(sl.pkts)
|
||||
}
|
||||
|
||||
func (sl *sentlist) AddPacket(datalen, off, bufsize int, seq Value, sentAt uint32) *ringidx {
|
||||
func (sl *sentlist) AddPacket(datalen, off, bufsize int, seq Value) *ringidx {
|
||||
free := sl.Free()
|
||||
if free == 0 {
|
||||
panic("pkt buffer full")
|
||||
@@ -361,11 +341,10 @@ func (sl *sentlist) AddPacket(datalen, off, bufsize int, seq Value, sentAt uint3
|
||||
panic("new sent packet offset must match last sent packet end")
|
||||
}
|
||||
sl.pkts = append(sl.pkts, ringidx{
|
||||
off: off,
|
||||
end: addEnd(off, datalen, bufsize),
|
||||
seq: seq,
|
||||
size: Size(datalen),
|
||||
sentAt: sentAt,
|
||||
off: off,
|
||||
end: addEnd(off, datalen, bufsize),
|
||||
seq: seq,
|
||||
size: Size(datalen),
|
||||
})
|
||||
return &sl.pkts[len(sl.pkts)-1]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user