Skip to content

Commit

Permalink
Check size of tcpFragmentList before delete fragment (seladb#1346)
Browse files Browse the repository at this point in the history
  • Loading branch information
MCredbear authored Jun 25, 2024
1 parent 2b22d02 commit 959369d
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 47 deletions.
21 changes: 21 additions & 0 deletions Packet++/header/TcpReassembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,26 @@ class TcpReassembly
TcpReassemblyData() : closed(false), numOfSides(0), prevSide(-1) {}
};

class OutOfOrderProcessingGuard
{
private:
bool& m_Flag;
public:
explicit OutOfOrderProcessingGuard(bool& flag) : m_Flag(flag)
{
m_Flag = true;
}

~OutOfOrderProcessingGuard()
{
m_Flag = false;
}

// Disable copy and move operations
OutOfOrderProcessingGuard(const OutOfOrderProcessingGuard&) = delete;
OutOfOrderProcessingGuard& operator=(const OutOfOrderProcessingGuard&) = delete;
};

typedef std::unordered_map<uint32_t, TcpReassemblyData> ConnectionList;
typedef std::map<time_t, std::list<uint32_t> > CleanupList;

Expand All @@ -445,6 +465,7 @@ class TcpReassembly
size_t m_MaxOutOfOrderFragments;
time_t m_PurgeTimepoint;
bool m_EnableBaseBufferClearCondition;
bool m_ProcessingOutOfOrder = false;

void checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList);

Expand Down
88 changes: 41 additions & 47 deletions Packet++/src/TcpReassembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(Packet& tcpData)
// if this side already got FIN or RST packet before, ignore this packet as this side is considered closed
if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst)
{
PCPP_LOG_DEBUG("Got a packet after FIN or RST were already seen on this side (" << sideIndex << "). Ignoring this packet");
PCPP_LOG_DEBUG("Got a packet after FIN or RST were already seen on this side (" << static_cast<int>(sideIndex) << "). Ignoring this packet");
return Ignore_PacketOfClosedFlow;
}

Expand Down Expand Up @@ -256,7 +256,7 @@ TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(Packet& tcpData)
if (m_EnableBaseBufferClearCondition && !first && tcpPayloadSize > 0 && tcpReassemblyData->prevSide != -1 && tcpReassemblyData->prevSide != sideIndex &&
tcpReassemblyData->twoSides[tcpReassemblyData->prevSide].tcpFragmentList.size() > 0)
{
PCPP_LOG_DEBUG("Seeing a first data packet from a different side. Previous side was " << tcpReassemblyData->prevSide << ", current side is " << sideIndex);
PCPP_LOG_DEBUG("Seeing a first data packet from a different side. Previous side was " << static_cast<int>(tcpReassemblyData->prevSide) << ", current side is " << static_cast<int>(sideIndex));
checkOutOfOrderFragments(tcpReassemblyData, tcpReassemblyData->prevSide, true);
}
tcpReassemblyData->prevSide = sideIndex;
Expand Down Expand Up @@ -412,7 +412,7 @@ TcpReassembly::ReassemblyStatus TcpReassembly::reassemblePacket(Packet& tcpData)
memcpy(newTcpFrag->data, tcpLayer->getLayerPayload(), tcpPayloadSize);
tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.pushBack(newTcpFrag);

PCPP_LOG_DEBUG("Found out-of-order packet and added a new TCP fragment with size " << tcpPayloadSize << " to the out-of-order list of side " << sideIndex);
PCPP_LOG_DEBUG("Found out-of-order packet and added a new TCP fragment with size " << tcpPayloadSize << " to the out-of-order list of side " << static_cast<int>(sideIndex));
status = OutOfOrderTcpMessageBuffered;

// check if we've stored too many out-of-order fragments; if so, consider missing packets lost and
Expand Down Expand Up @@ -451,7 +451,7 @@ void TcpReassembly::handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t
if (tcpReassemblyData->twoSides[sideIndex].gotFinOrRst)
return;

PCPP_LOG_DEBUG("Handling FIN or RST packet on side " << sideIndex);
PCPP_LOG_DEBUG("Handling FIN or RST packet on side " << static_cast<int>(sideIndex));

// set FIN/RST flag for this side
tcpReassemblyData->twoSides[sideIndex].gotFinOrRst = true;
Expand All @@ -473,34 +473,42 @@ void TcpReassembly::handleFinOrRst(TcpReassemblyData* tcpReassemblyData, int8_t

void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyData, int8_t sideIndex, bool cleanWholeFragList)
{
if (m_ProcessingOutOfOrder)
{
return;
}

OutOfOrderProcessingGuard guard(m_ProcessingOutOfOrder);

bool foundSomething = false;

auto& curSideData = tcpReassemblyData->twoSides[sideIndex];

do
{
PCPP_LOG_DEBUG("Starting first iteration of checkOutOfOrderFragments - looking for fragments that match the current sequence or have smaller sequence");

int index = 0;
foundSomething = false;

do
{
index = 0;
auto tcpFragIter = curSideData.tcpFragmentList.begin();
foundSomething = false;

// first fragment list iteration - go over the whole fragment list and see if can find fragments that match the current sequence
// or have smaller sequence but have big enough payload to get new data
while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size())
while (tcpFragIter != curSideData.tcpFragmentList.end())
{
TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index);

// if fragment sequence matches the current sequence
if (curTcpFrag->sequence == tcpReassemblyData->twoSides[sideIndex].sequence)
if ((*tcpFragIter)->sequence == curSideData.sequence)
{
// pop the fragment from fragment list
auto curTcpFrag = std::unique_ptr<TcpFragment>(curSideData.tcpFragmentList.getAndRemoveFromVector(tcpFragIter));
// update sequence
tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength;
curSideData.sequence += curTcpFrag->dataLength;
if (curTcpFrag->data != nullptr)
{
PCPP_LOG_DEBUG("Found an out-of-order packet matching to the current sequence with size " << curTcpFrag->dataLength << " on side " << sideIndex << ". Pulling it out of the list and sending the data to the callback");
PCPP_LOG_DEBUG("Found an out-of-order packet matching to the current sequence with size " << curTcpFrag->dataLength << " on side " << static_cast<int>(sideIndex) << ". Pulling it out of the list and sending the data to the callback");

// send new data to callback

Expand All @@ -511,32 +519,30 @@ void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyDat
}
}


// remove fragment from list
tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index);

foundSomething = true;

continue;
}

// if fragment sequence has lower sequence than the current sequence
if (SEQ_LT(curTcpFrag->sequence, tcpReassemblyData->twoSides[sideIndex].sequence))
if (SEQ_LT((*tcpFragIter)->sequence, curSideData.sequence))
{
// pop the fragment from fragment list
auto curTcpFrag = std::unique_ptr<TcpFragment>(curSideData.tcpFragmentList.getAndRemoveFromVector(tcpFragIter));
// check if it still has new data
uint32_t newSequence = curTcpFrag->sequence + curTcpFrag->dataLength;

// it has new data
if (SEQ_GT(newSequence, tcpReassemblyData->twoSides[sideIndex].sequence))
if (SEQ_GT(newSequence, curSideData.sequence))
{
// calculate the delta new data size
uint32_t newLength = tcpReassemblyData->twoSides[sideIndex].sequence - curTcpFrag->sequence;
uint32_t newLength = curSideData.sequence - curTcpFrag->sequence;

PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which its sequence is lower than expected but its payload is long enough to contain new data. "
"Calling the callback with the new data. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex << ", new data size is " << (int)(curTcpFrag->dataLength - newLength));
"Calling the callback with the new data. Fragment size is " << curTcpFrag->dataLength << " on side " << static_cast<int>(sideIndex) << ", new data size is " << static_cast<int>(curTcpFrag->dataLength - newLength));

// update current sequence with the delta new data size
tcpReassemblyData->twoSides[sideIndex].sequence += curTcpFrag->dataLength - newLength;
curSideData.sequence += curTcpFrag->dataLength - newLength;

// send only the new data to the callback
if (m_OnMessageReadyCallback != nullptr)
Expand All @@ -549,17 +555,14 @@ void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyDat
}
else
{
PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which doesn't contain any new data, ignoring it. Fragment size is " << curTcpFrag->dataLength << " on side " << sideIndex);
PCPP_LOG_DEBUG("Found a fragment in the out-of-order list which doesn't contain any new data, ignoring it. Fragment size is " << curTcpFrag->dataLength << " on side " << static_cast<int>(sideIndex));
}

// delete fragment from list
tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + index);

continue;
}

//if got to here it means the fragment has higher sequence than current sequence, increment index and continue
index++;
//if got to here it means the fragment has higher sequence than current sequence, increment it and continue
tcpFragIter++;
}

// if managed to find new segment, do the search all over again
Expand All @@ -569,7 +572,7 @@ void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyDat
// if got here it means we're left only with fragments that have higher sequence than current sequence. This means out-of-order packets or
// missing data. If we don't want to clear the frag list yet and the number of out of order fragments isn't above the configured limit,
// assume it's out-of-order and return
if (!cleanWholeFragList && (m_MaxOutOfOrderFragments == 0 || tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size() <= m_MaxOutOfOrderFragments))
if (!cleanWholeFragList && (m_MaxOutOfOrderFragments == 0 || curSideData.tcpFragmentList.size() <= m_MaxOutOfOrderFragments))
{
return;
}
Expand All @@ -581,36 +584,30 @@ void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyDat

uint32_t closestSequence = 0xffffffff;
bool closestSequenceDefined = false;
int closestSequenceFragIndex = -1;
index = 0;
auto closestSequenceFragIt = curSideData.tcpFragmentList.end();

while (index < (int)tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.size())
for (auto tcpFragIter = curSideData.tcpFragmentList.begin(); tcpFragIter != curSideData.tcpFragmentList.end(); tcpFragIter++)
{
// extract segment at current index
TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(index);

// check if its sequence is closer than current closest sequence
if (!closestSequenceDefined || SEQ_LT(curTcpFrag->sequence, closestSequence))
if (!closestSequenceDefined || SEQ_LT((*tcpFragIter)->sequence, closestSequence))
{
closestSequence = curTcpFrag->sequence;
closestSequenceFragIndex = index;
closestSequence = (*tcpFragIter)->sequence;
closestSequenceFragIt = tcpFragIter;
closestSequenceDefined = true;
}

index++;
}

// this means fragment list is not empty at this stage
if (closestSequenceFragIndex > -1)
if (closestSequenceFragIt != curSideData.tcpFragmentList.end())
{
// get the fragment with the closest sequence
TcpFragment* curTcpFrag = tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.at(closestSequenceFragIndex);
auto curTcpFrag = std::unique_ptr<TcpFragment>(curSideData.tcpFragmentList.getAndRemoveFromVector(closestSequenceFragIt));

// calculate number of missing bytes
uint32_t missingDataLen = curTcpFrag->sequence - tcpReassemblyData->twoSides[sideIndex].sequence;
uint32_t missingDataLen = curTcpFrag->sequence - curSideData.sequence;

// update sequence
tcpReassemblyData->twoSides[sideIndex].sequence = curTcpFrag->sequence + curTcpFrag->dataLength;
curSideData.sequence = curTcpFrag->sequence + curTcpFrag->dataLength;
if (curTcpFrag->data != nullptr)
{
// send new data to callback
Expand All @@ -630,13 +627,10 @@ void TcpReassembly::checkOutOfOrderFragments(TcpReassemblyData* tcpReassemblyDat
TcpStreamData streamData(&dataWithMissingDataText[0], dataWithMissingDataText.size(), missingDataLen, tcpReassemblyData->connData, curTcpFrag->timestamp);
m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie);

PCPP_LOG_DEBUG("Found missing data on side " << sideIndex << ": " << missingDataLen << " byte are missing. Sending the closest fragment which is in size " << curTcpFrag->dataLength << " + missing text message which size is " << missingDataTextStr.length());
PCPP_LOG_DEBUG("Found missing data on side " << static_cast<int>(sideIndex) << ": " << missingDataLen << " byte are missing. Sending the closest fragment which is in size " << curTcpFrag->dataLength << " + missing text message which size is " << missingDataTextStr.length());
}
}

// remove fragment from list
tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.erase(tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.begin() + closestSequenceFragIndex);

PCPP_LOG_DEBUG("Calling checkOutOfOrderFragments again from the start");

// call the method again from the start to do the whole search again (both iterations).
Expand Down
Loading

0 comments on commit 959369d

Please sign in to comment.