mirror of
https://github.com/soypat/lneto.git
synced 2026-08-20 14:39:02 +00:00
TCP retransmission fixes and fuzz failure fixes (#59)
* fix for #58 * add fixes for both retransmit and fuzz failure found * fix infinite challenge ack due to RST+SYN+FIN corrupt packet in SYNRCV state * fuzz: previous recovery ack path led to infinite transmit * fuzz: calculate CRCs when fuzzing to reduce search space to non-CRC error cases * add local fuzz corpus to tests * add more cases to fuzz corpus
This commit is contained in:
+24
-19
@@ -438,7 +438,8 @@ func (tcb *ControlBlock) validateIncomingSegment(seg Segment) (err error) {
|
||||
// Short circuit SEQ checks if SYN present in pre-established states only.
|
||||
// In synchronized states SYN must pass normal SEQ validation (RFC 9293 §3.10.7.4).
|
||||
preestablished := tcb._state.IsPreestablished()
|
||||
checkSEQ := !flags.HasAny(FlagSYN) || !preestablished
|
||||
// LISTEN has no receive window context; RFC 9293 §3.10.7.1 step 1: "no checking in LISTEN state."
|
||||
checkSEQ := (!flags.HasAny(FlagSYN) || !preestablished) && tcb._state != StateListen
|
||||
established := tcb._state == StateEstablished
|
||||
acksOld := hasAck && !tcb.snd.UNA.LessThan(seg.ACK)
|
||||
acksUnsentData := hasAck && !seg.ACK.LessThanEq(tcb.snd.NXT)
|
||||
@@ -493,24 +494,15 @@ func (tcb *ControlBlock) validateIncomingSegment(seg Segment) (err error) {
|
||||
}
|
||||
|
||||
case established && acksUnsentData:
|
||||
// After Retransmit() rewinds snd.NXT to snd.UNA, the remote may ACK
|
||||
// data it received pre-rewind — a valid cumulative ACK that exceeds
|
||||
// the rewound snd.NXT. Detect this case (NXT==UNA means rewind active)
|
||||
// and accept the ACK if within the send window.
|
||||
retransmitActive := tcb.snd.NXT == tcb.snd.UNA
|
||||
if retransmitActive && seg.ACK.InWindow(tcb.snd.UNA, tcb.snd.WND) {
|
||||
tcb.snd.NXT = seg.ACK
|
||||
if isDebug {
|
||||
tcb.debug("rcv:ACK-advance-nxt", slog.String("state", tcb._state.String()),
|
||||
slog.Uint64("seg.ack", uint64(seg.ACK)), slog.Uint64("snd.nxt", uint64(tcb.snd.NXT)))
|
||||
}
|
||||
} else {
|
||||
err = errDropSegment
|
||||
tcb.pending[0] |= FlagACK // Send ACK for unsent data; |= preserves any pending FIN.
|
||||
if isDebug {
|
||||
tcb.debug("rcv:ACK-unsent", slog.String("state", tcb._state.String()),
|
||||
slog.Uint64("seg.ack", uint64(seg.ACK)), slog.Uint64("snd.nxt", uint64(tcb.snd.NXT)))
|
||||
}
|
||||
// ACK for data we haven't sent. Drop and send challenge ACK.
|
||||
// Note: after Retransmit() rewinds snd.NXT, a cumulative ACK may exceed
|
||||
// the rewound NXT. That case is handled by Handler.RecoveryACK, not here —
|
||||
// NXT==UNA is ambiguous (also true when no data is in flight).
|
||||
err = errDropSegment
|
||||
tcb.pending[0] |= FlagACK // Send ACK for unsent data; |= preserves any pending FIN.
|
||||
if isDebug {
|
||||
tcb.debug("rcv:ACK-unsent", slog.String("state", tcb._state.String()),
|
||||
slog.Uint64("seg.ack", uint64(seg.ACK)), slog.Uint64("snd.nxt", uint64(tcb.snd.NXT)))
|
||||
}
|
||||
|
||||
case preestablished && (acksOld || acksUnsentData):
|
||||
@@ -581,6 +573,19 @@ func (tcb *ControlBlock) rstJump() Value {
|
||||
// Implements RFC 9293 §3.10.8 (RETRANSMISSION TIMEOUT).
|
||||
func (tcb *ControlBlock) Retransmit() { tcb.snd.NXT = tcb.snd.UNA }
|
||||
|
||||
// RecoveryACK accepts a cumulative ACK that covers data sent before a retransmit
|
||||
// rewind. After Retransmit() rewinds snd.NXT, the remote may ACK data it received
|
||||
// pre-rewind — a valid cumulative ACK that exceeds the rewound snd.NXT. This method
|
||||
// advances snd.UNA, snd.NXT and updates the send window from the segment.
|
||||
// The caller must verify that seg.ACK is within the pre-rewind NXT range.
|
||||
func (tcb *ControlBlock) RecoveryACK(seg Segment) {
|
||||
tcb.snd.UNA = seg.ACK
|
||||
tcb.snd.NXT = seg.ACK
|
||||
tcb.snd.WND = seg.WND
|
||||
// Clear any pending ACK that validateIncomingSegment queued on rejection.
|
||||
tcb.pending[0] &^= FlagACK
|
||||
}
|
||||
|
||||
// Abort sets ControlBlock state to Closed and resets all sequence numbers and pending flag.
|
||||
// No more data can be sent nor received after the connection is aborted until opened again.
|
||||
// An abort call prepares the connection for opening an active connection via a
|
||||
|
||||
Reference in New Issue
Block a user