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 support for timestamp related options (#1656) #1657

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 6 additions & 2 deletions Common++/src/SystemUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ namespace pcpp

bool directoryExists(const std::string& dirPath)
{
struct stat info{};
struct stat info
{
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we revert the format?

};

if (stat(dirPath.c_str(), &info) != 0)
{
Expand Down Expand Up @@ -368,7 +370,9 @@ namespace pcpp
#if defined(_WIN32)
SetConsoleCtrlHandler((PHANDLER_ROUTINE)handlerRoutine, TRUE);
#else
struct sigaction action{};
struct sigaction action
{
};
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = handlerRoutine;
sigemptyset(&action.sa_mask);
Expand Down
3 changes: 2 additions & 1 deletion Packet++/src/DnsResourceData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ namespace pcpp
{
if (m_DataLen == 0 || m_Data == nullptr)
{
PCPP_LOG_ERROR("Input data is null or illegal" << "|m_DataLen:" << m_DataLen);
PCPP_LOG_ERROR("Input data is null or illegal"
<< "|m_DataLen:" << m_DataLen);
Copy link
Collaborator

Choose a reason for hiding this comment

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

any reason to change the format?

return false;
}

Expand Down
3 changes: 2 additions & 1 deletion Packet++/src/VrrpLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,8 @@ namespace pcpp
auto* ipLayer = m_Packet->getLayerOfType<pcpp::IPLayer>();
if (ipLayer == nullptr)
{
PCPP_LOG_ERROR("Calculate checksum failed, for can not get IPLayer" << "");
PCPP_LOG_ERROR("Calculate checksum failed, for can not get IPLayer"
<< "");
return 0;
}

Expand Down
42 changes: 41 additions & 1 deletion Pcap++/header/PcapLiveDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,34 @@ namespace pcpp
PCPP_OUT
};

/// Set which source provides timestamps associated to each captured packet
/// (you can read more here: <https://www.tcpdump.org/manpages/pcap-tstamp.7.html>)
enum class TimestampProvider
{
/** host-provided, unknown characteristics, default */
Copy link
Collaborator

Choose a reason for hiding this comment

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

let use /// directly.

Host = 0,
/** host-provided, low precision, synced with the system clock */
HostLowPrec,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Prec seems not to be a common abbreviation. Maybe it's better to spell out the full Precision.

/** host-provided, high precision, synced with the system clock */
HostHighPrec,
/** device-provided, synced with the system clock */
Adapter,
/** device-provided, not synced with the system clock */
AdapterUnsynced,
/** host-provided, high precision, not synced with the system clock */
HostHighPrecUnsynced
};

/// Set the precision of timestamps associated to each captured packet
/// (you can read more here: <https://www.tcpdump.org/manpages/pcap-tstamp.7.html>)
enum class TimestampPrecision
{
/** use timestamps with microsecond precision, default */
Microseconds = 0,
/** use timestamps with nanosecond precision */
Nanoseconds,
};

/// @struct DeviceConfiguration
/// A struct that contains user configurable parameters for opening a device. All parameters have default values
/// so the user isn't expected to set all parameters or understand exactly how they work
Expand Down Expand Up @@ -208,6 +236,14 @@ namespace pcpp
/// In Unix-like system, use poll() for blocking mode.
bool usePoll;

/// Set which timestamp provider is used.
/// Depending on the capture device and the software on the host, different types of time stamp can be used
TimestampProvider timestampProvider;

/// Set which timestamp precision is used.
/// Depending on the capture device and the software on the host, different precision can be used
TimestampPrecision timestampPrecision;

/// A c'tor for this struct
/// @param[in] mode The mode to open the device: promiscuous or non-promiscuous. Default value is
/// promiscuous
Expand All @@ -226,7 +262,9 @@ namespace pcpp
/// on Unix-like system. Default value is false.
explicit DeviceConfiguration(DeviceMode mode = Promiscuous, int packetBufferTimeoutMs = 0,
int packetBufferSize = 0, PcapDirection direction = PCPP_INOUT,
int snapshotLength = 0, unsigned int nflogGroup = 0, bool usePoll = false)
int snapshotLength = 0, unsigned int nflogGroup = 0, bool usePoll = false,
TimestampProvider timestampProvider = TimestampProvider::Host,
TimestampPrecision timestampPrecision = TimestampPrecision::Microseconds)
{
this->mode = mode;
this->packetBufferTimeoutMs = packetBufferTimeoutMs;
Expand All @@ -235,6 +273,8 @@ namespace pcpp
this->snapshotLength = snapshotLength;
this->nflogGroup = nflogGroup;
this->usePoll = usePoll;
this->timestampProvider = timestampProvider;
this->timestampPrecision = timestampPrecision;
}
};

Expand Down
133 changes: 133 additions & 0 deletions Pcap++/src/PcapLiveDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
static const char* NFLOG_IFACE = "nflog";
static const int DEFAULT_SNAPLEN = 9000;

#ifndef PCAP_TSTAMP_HOST_HIPREC_UNSYNCED
// PCAP_TSTAMP_HOST_HIPREC_UNSYNCED defined only in libpcap > 1.10.0
# define PCAP_TSTAMP_HOST_HIPREC_UNSYNCED 5
#endif

namespace pcpp
{

Expand All @@ -86,6 +91,124 @@ namespace pcpp
}
#endif

#if defined(_WIN32)
static void setTimestampProvider(pcap_t* pcap, const PcapLiveDevice::TimestampProvider timestampProvider)
{
PCPP_LOG_ERROR("Windows doesn't support timestampProvider option");
}

static void setTimestampPrecision(pcap_t* pcap, const PcapLiveDevice::TimestampPrecision timestampPrecision)
{
PCPP_LOG_ERROR("Windows doesn't support timestampPrecision option");
}
#else
static int timestampProviderMap(const PcapLiveDevice::TimestampProvider timestampProvider)
{
switch (timestampProvider)
{
case PcapLiveDevice::TimestampProvider::Host:
return PCAP_TSTAMP_HOST;
case PcapLiveDevice::TimestampProvider::HostLowPrec:
return PCAP_TSTAMP_HOST_LOWPREC;
case PcapLiveDevice::TimestampProvider::HostHighPrec:
return PCAP_TSTAMP_HOST_HIPREC;
case PcapLiveDevice::TimestampProvider::Adapter:
return PCAP_TSTAMP_ADAPTER;
case PcapLiveDevice::TimestampProvider::AdapterUnsynced:
return PCAP_TSTAMP_ADAPTER_UNSYNCED;
case PcapLiveDevice::TimestampProvider::HostHighPrecUnsynced:
return PCAP_TSTAMP_HOST_HIPREC_UNSYNCED;
}
return PCAP_TSTAMP_HOST;
}

static int timestampPrecisionMap(const PcapLiveDevice::TimestampPrecision timestampPrecision)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe rename this timestampPrecisionMap? like GetPcapPrecision?

{
switch (timestampPrecision)
{
case PcapLiveDevice::TimestampPrecision::Microseconds:
return PCAP_TSTAMP_PRECISION_MICRO;
case PcapLiveDevice::TimestampPrecision::Nanoseconds:
return PCAP_TSTAMP_PRECISION_NANO;
}
return PCAP_TSTAMP_PRECISION_MICRO;
}

static bool isTimestampProviderSupportedByDevice(pcap_t* pcap,
const PcapLiveDevice::TimestampProvider timestampProvider)
{
const auto tstampType = timestampProviderMap(timestampProvider);

// Use unique_ptr with a custom deleter directly
std::unique_ptr<int[], void (*)(int*)> supportedTstampTypes(nullptr, [](int* ptr) {
if (ptr != nullptr)
{
pcap_free_tstamp_types(ptr);
}
});

const int numSupportedTstampTypes = pcap_list_tstamp_types(pcap, &supportedTstampTypes);
Copy link
Collaborator

@Dimi1010 Dimi1010 Dec 23, 2024

Choose a reason for hiding this comment

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

It might be useful to wrap supportedTstampTypes into a std::unique_ptr with a custom deleter, as that would handle the deallocation automatically when the ptr goes out of scope.


if (numSupportedTstampTypes < 0)
{
std::cerr << "Error retrieving timestamp types: " << pcap_geterr(pcap) << " - default Host will be used"
<< std::endl;
return false;
}

if (numSupportedTstampTypes == 1)
{
// If 1 is returned, then the only available timestamp is TimestampProvider::Host
return timestampProvider == PcapLiveDevice::TimestampProvider::Host;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we not have to free supportedTstampTypes here?

}

for (int i = 0; i < numSupportedTstampTypes; ++i)
{
if (supportedTstampTypes[i] == tstampType)
{
return true;
}
}

return false;
}

static void setTimestampProvider(pcap_t* pcap, const PcapLiveDevice::TimestampProvider timestampProvider)
{
if (isTimestampProviderSupportedByDevice(pcap, timestampProvider))
{
const int ret = pcap_set_tstamp_type(pcap, timestampProviderMap(timestampProvider));
if (ret == 0)
{
PCPP_LOG_DEBUG("Timestamp provider was set");
}
else
{
PCPP_LOG_ERROR("Failed to set timestamping provider: '" << ret << "', error message: '"
<< pcap_geterr(pcap) << "'");
}
}
else
{
PCPP_LOG_ERROR("Selected timestamping provider is not supported");
}
}

static void setTimestampPrecision(pcap_t* pcap, const PcapLiveDevice::TimestampPrecision timestampPrecision)
{
const int ret = pcap_set_tstamp_precision(pcap, timestampPrecisionMap(timestampPrecision));
if (ret == 0)
{
PCPP_LOG_DEBUG("Timestamp precision was set");
}
else
{
PCPP_LOG_ERROR("Failed to set timestamping precision: '" << ret << "', error message: '"
<< pcap_geterr(pcap) << "'");
}
}
#endif

PcapLiveDevice::DeviceInterfaceDetails::DeviceInterfaceDetails(pcap_if_t* pInterface)
: name(pInterface->name), isLoopback(pInterface->flags & PCAP_IF_LOOPBACK)
{
Expand Down Expand Up @@ -310,6 +433,16 @@ namespace pcpp
}
#endif

if (config.timestampProvider != PcapLiveDevice::TimestampProvider::Host)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it make sense to always bind config.timestampProvider and config.timestampPrecision together?

{
setTimestampProvider(pcap, config.timestampProvider);
}

if (config.timestampPrecision != PcapLiveDevice::TimestampPrecision::Nanoseconds)
{
setTimestampPrecision(pcap, config.timestampPrecision);
}

ret = pcap_activate(pcap);
if (ret != 0)
{
Expand Down