Skip to content

Commit 5ada3ae

Browse files
committed
tcp: (fixes #1349) Detect lost retransmissions via SACK of later-sent data
Assisted-by: Claude Fable 5
1 parent 948aa03 commit 5ada3ae

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

src/internet/model/tcp-tx-buffer.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ TcpTxBuffer::Update(const TcpOptionSack::SackList& list, const Callback<void, Tc
774774
NS_LOG_INFO("Updating scoreboard, got " << list.size() << " blocks to analyze");
775775

776776
uint32_t bytesSacked = 0;
777+
Time newestSackedSentTime = Time::Min();
777778

778779
for (auto option_it = list.begin(); option_it != list.end(); ++option_it)
779780
{
@@ -827,6 +828,7 @@ TcpTxBuffer::Update(const TcpOptionSack::SackList& list, const Callback<void, Tc
827828
(*item_it)->m_sacked = true;
828829
m_sackedOut += (*item_it)->m_packet->GetSize();
829830
bytesSacked += (*item_it)->m_packet->GetSize();
831+
newestSackedSentTime = std::max(newestSackedSentTime, (*item_it)->m_lastSent);
830832

831833
if (m_highestSack.first == m_sentList.end() ||
832834
m_highestSack.second <= beginOfCurrentPacket + pktSize)
@@ -862,6 +864,7 @@ TcpTxBuffer::Update(const TcpOptionSack::SackList& list, const Callback<void, Tc
862864
if (bytesSacked > 0)
863865
{
864866
NS_ASSERT_MSG(m_highestSack.first != m_sentList.end(), "Buffer status: " << *this);
867+
MarkRetransmittedSegmentsLost(newestSackedSentTime);
865868
UpdateLostCount();
866869
}
867870

@@ -922,6 +925,35 @@ TcpTxBuffer::UpdateLostCount()
922925
ConsistencyCheck();
923926
}
924927

928+
void
929+
TcpTxBuffer::MarkRetransmittedSegmentsLost(const Time& sackedSentTime)
930+
{
931+
NS_LOG_FUNCTION(this << sackedSentTime);
932+
933+
SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
934+
for (auto it = m_sentList.begin(); it != m_sentList.end(); ++it)
935+
{
936+
TcpTxItem* item = *it;
937+
if (beginOfCurrentPacket >= m_highestSack.second)
938+
{
939+
break;
940+
}
941+
if (item->m_retrans && !item->m_sacked && item->m_lastSent < sackedSentTime)
942+
{
943+
item->m_retrans = false;
944+
m_retrans -= item->m_packet->GetSize();
945+
if (!item->m_lost)
946+
{
947+
item->m_lost = true;
948+
m_lostOut += item->m_packet->GetSize();
949+
}
950+
NS_LOG_INFO("Retransmission of segment " << *item << " deemed lost");
951+
}
952+
beginOfCurrentPacket += item->m_packet->GetSize();
953+
}
954+
ConsistencyCheck();
955+
}
956+
925957
bool
926958
TcpTxBuffer::IsLost(const SequenceNumber32& seq) const
927959
{

src/internet/model/tcp-tx-buffer.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,27 @@ class TcpTxBuffer : public Object
424424
*/
425425
void UpdateLostCount();
426426

427+
/**
428+
* @brief Mark as lost the retransmitted segments that were sent before
429+
* a newly SACKed segment
430+
*
431+
* A SACK for a segment that was transmitted after a retransmission
432+
* implies that the retransmission itself was dropped by the network:
433+
* had it been delivered, the receiver would have acknowledged it before
434+
* generating the SACK for the later segment. This is the time-based
435+
* loss detection of RFC 8985 (RACK) applied to retransmissions, with a
436+
* zero reordering window; Linux implemented the equivalent
437+
* sequence-based heuristic as tcp_mark_lost_retrans() before adopting
438+
* RACK. Clearing the retransmitted flag makes the segment eligible
439+
* again for retransmission via NextSeg(); without this detection, a
440+
* lost retransmission stalls the connection until the retransmission
441+
* timeout fires.
442+
*
443+
* @param sackedSentTime Latest transmission time among the newly SACKed
444+
* segments
445+
*/
446+
void MarkRetransmittedSegmentsLost(const Time& sackedSentTime);
447+
427448
/**
428449
* @brief Remove the size specified from the lostOut, retrans, sacked count
429450
*

0 commit comments

Comments
 (0)