Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nanosecond precision to TCP reassembly #1591

Merged
merged 9 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions Packet++/header/TcpReassembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "IpAddress.h"
#include "PointerVector.h"
#include <unordered_map>
#include <chrono>
#include <map>
#include <list>
#include <time.h>
Expand Down Expand Up @@ -106,10 +107,14 @@ namespace pcpp
uint16_t dstPort;
/** A 4-byte hash key representing the connection */
uint32_t flowKey;
/** Start TimeStamp of the connection */
/** Start timestamp of the connection with microsecond precision */
timeval startTime;
/** End TimeStamp of the connection */
/** End timestamp of the connection with microsecond precision */
timeval endTime;
/** Start timestamp of the connection with nanosecond precision */
std::chrono::time_point<std::chrono::high_resolution_clock> startTimePrecise;
/** End timestamp of the connection with nanosecond precision */
std::chrono::time_point<std::chrono::high_resolution_clock> endTimePrecise;

/**
* A c'tor for this struct that basically zeros all members
Expand All @@ -118,22 +123,16 @@ namespace pcpp
{}

/**
* Set startTime of Connection
* @param[in] startTimeValue integer value
* Set the start time of the connection
* @param[in] startTimeValue timestamp value
*/
void setStartTime(const timeval& startTimeValue)
{
startTime = startTimeValue;
}
void setStartTime(const std::chrono::time_point<std::chrono::high_resolution_clock>& startTimeValue);

/**
* Set endTime of Connection
* @param[in] endTimeValue integer value
* Set the end time of the connection
* @param[in] endTimeValue timestamp value
*/
void setEndTime(const timeval& endTimeValue)
{
endTime = endTimeValue;
}
void setEndTime(const std::chrono::time_point<std::chrono::high_resolution_clock>& endTimeValue);
};

class TcpReassembly;
Expand All @@ -156,7 +155,7 @@ namespace pcpp
* @param[in] timestamp when this packet was received
*/
TcpStreamData(const uint8_t* tcpData, size_t tcpDataLength, size_t missingBytes, const ConnectionData& connData,
timeval timestamp)
std::chrono::time_point<std::chrono::high_resolution_clock> timestamp)
: m_Data(tcpData), m_DataLen(tcpDataLength), m_MissingBytes(missingBytes), m_Connection(connData),
m_Timestamp(timestamp)
{}
Expand Down Expand Up @@ -207,10 +206,14 @@ namespace pcpp
}

/**
* A getter for the timestamp of this packet
* @return The const timeval object with timestamp of this packet
* @return A microsecond precision of the packet timestamp
*/
timeval getTimeStamp() const;

/**
* @return A nanosecond precision of the packet timestamp
*/
timeval getTimeStamp() const
std::chrono::time_point<std::chrono::high_resolution_clock> getTimeStampPrecise() const
{
return m_Timestamp;
}
Expand All @@ -220,7 +223,7 @@ namespace pcpp
size_t m_DataLen;
size_t m_MissingBytes;
const ConnectionData& m_Connection;
timeval m_Timestamp;
std::chrono::time_point<std::chrono::high_resolution_clock> m_Timestamp;
};

/**
Expand Down Expand Up @@ -483,7 +486,7 @@ namespace pcpp
uint32_t sequence;
size_t dataLength;
uint8_t* data;
timeval timestamp;
std::chrono::time_point<std::chrono::high_resolution_clock> timestamp;

TcpFragment() : sequence(0), dataLength(0), data(nullptr)
{}
Expand Down
58 changes: 40 additions & 18 deletions Packet++/src/TcpReassembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,44 @@
namespace pcpp
{

static timeval timespecToTimeval(const timespec& in)
static timeval timePointToTimeval(const std::chrono::time_point<std::chrono::high_resolution_clock>& in)
{
timeval out;
TIMESPEC_TO_TIMEVAL(&out, &in);
auto duration = in.time_since_epoch();

auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(duration) % 1000000000;
Copy link
Collaborator

@Dimi1010 Dimi1010 Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could possibly be done without the magic number by casting the seconds to nanosecond precision and substracting that from the main duration to get the remaining nanoseconds, but I guess this works too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ture, I think Dimi's method will be more efficient.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ture, I think Dimi's method will be more efficient.

Looked through it in godbolt.org. It actually does not matter under optimizations. Both compile down to mostly the same assembly.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing compiler

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! Fixed in 91f6b39


struct timeval out;
out.tv_sec = seconds.count();
out.tv_usec = nanoseconds.count() / 1000;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we calculate the nanosecond precision here if we are just going to convert them to microseconds? As far as I see, the nanosecond precision duration object is not used anywhere else in this function.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed in 91f6b39

return out;
}

static std::chrono::time_point<std::chrono::high_resolution_clock> timespecToTimePoint(const timespec& in)
{
auto duration = std::chrono::duration_cast<std::chrono::high_resolution_clock::duration>(
std::chrono::seconds(in.tv_sec) + std::chrono::nanoseconds(in.tv_nsec));

return std::chrono::time_point<std::chrono::high_resolution_clock>(duration);
}

void ConnectionData::setStartTime(const std::chrono::time_point<std::chrono::high_resolution_clock>& startTimeValue)
{
startTime = timePointToTimeval(startTimeValue);
startTimePrecise = startTimeValue;
}

void ConnectionData::setEndTime(const std::chrono::time_point<std::chrono::high_resolution_clock>& endTimeValue)
{
endTime = timePointToTimeval(endTimeValue);
endTimePrecise = endTimeValue;
}

timeval TcpStreamData::getTimeStamp() const
{
return timePointToTimeval(m_Timestamp);
}

TcpReassembly::TcpReassembly(OnTcpMessageReady onMessageReadyCallback, void* userCookie,
OnTcpConnectionStart onConnectionStartCallback,
OnTcpConnectionEnd onConnectionEndCallback, const TcpReassemblyConfiguration& config)
Expand Down Expand Up @@ -109,7 +140,7 @@ namespace pcpp
uint32_t flowKey = hash5Tuple(&tcpData);

// time stamp for this packet
timeval currTime = timespecToTimeval(tcpData.getRawPacket()->getPacketTimeStamp());
auto currTime = timespecToTimePoint(tcpData.getRawPacket()->getPacketTimeStamp());

// find the connection in the connection map
ConnectionList::iterator iter = m_ConnectionList.find(flowKey);
Expand Down Expand Up @@ -146,22 +177,13 @@ namespace pcpp

tcpReassemblyData = &iter->second;

if (currTime.tv_sec > tcpReassemblyData->connData.endTime.tv_sec)
if (currTime > tcpReassemblyData->connData.endTimePrecise)
{
tcpReassemblyData->connData.setEndTime(currTime);
m_ConnectionInfo[flowKey].setEndTime(currTime);
}
else if (currTime.tv_sec == tcpReassemblyData->connData.endTime.tv_sec)
{
if (currTime.tv_usec > tcpReassemblyData->connData.endTime.tv_usec)
{
tcpReassemblyData->connData.setEndTime(currTime);
m_ConnectionInfo[flowKey].setEndTime(currTime);
}
}
}

timeval timestampOfTheReceivedPacket = currTime;
int8_t sideIndex = -1;
bool first = false;

Expand Down Expand Up @@ -299,7 +321,7 @@ namespace pcpp
if (tcpPayloadSize != 0 && m_OnMessageReadyCallback != nullptr)
{
TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData,
timestampOfTheReceivedPacket);
currTime);
m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie);
}
status = TcpMessageHandled;
Expand Down Expand Up @@ -337,7 +359,7 @@ namespace pcpp
if (m_OnMessageReadyCallback != nullptr)
{
TcpStreamData streamData(tcpLayer->getLayerPayload() + newLength, tcpPayloadSize - newLength, 0,
tcpReassemblyData->connData, timestampOfTheReceivedPacket);
tcpReassemblyData->connData, currTime);
m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie);
}
status = TcpMessageHandled;
Expand Down Expand Up @@ -390,7 +412,7 @@ namespace pcpp
if (m_OnMessageReadyCallback != nullptr)
{
TcpStreamData streamData(tcpLayer->getLayerPayload(), tcpPayloadSize, 0, tcpReassemblyData->connData,
timestampOfTheReceivedPacket);
currTime);
m_OnMessageReadyCallback(sideIndex, streamData, m_UserCookie);
}
status = TcpMessageHandled;
Expand Down Expand Up @@ -436,7 +458,7 @@ namespace pcpp
newTcpFrag->data = new uint8_t[tcpPayloadSize];
newTcpFrag->dataLength = tcpPayloadSize;
newTcpFrag->sequence = sequence;
newTcpFrag->timestamp = timestampOfTheReceivedPacket;
newTcpFrag->timestamp = currTime;
memcpy(newTcpFrag->data, tcpLayer->getLayerPayload(), tcpPayloadSize);
tcpReassemblyData->twoSides[sideIndex].tcpFragmentList.pushBack(newTcpFrag);

Expand Down
1 change: 1 addition & 0 deletions Tests/Pcap++Test/TestDefinition.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ PTF_TEST_CASE(TestTcpReassemblyMaxSeq);
PTF_TEST_CASE(TestTcpReassemblyDisableOOOCleanup);
PTF_TEST_CASE(TestTcpReassemblyTimeStamps);
PTF_TEST_CASE(TestTcpReassemblyFinReset);
PTF_TEST_CASE(TestTcpReassemblyHighPrecision);

// Implemented in IPFragmentationTests.cpp
PTF_TEST_CASE(TestIPFragmentationSanity);
Expand Down
Loading
Loading