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 1 commit 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
53 changes: 52 additions & 1 deletion Pcap++/header/PcapLiveDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,38 @@ 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 */
Host = 0,
/** host-provided, low precision, synced with the system clock */
HostLowPrec,
/** 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
Expand Down Expand Up @@ -236,6 +268,18 @@ 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 @@ -252,10 +296,15 @@ namespace pcpp
* @param[in] nflogGroup NFLOG group for NFLOG devices. Default value is 0.
* @param[in] usePoll use `poll()` when capturing packets in blocking more (`startCaptureBlockingMode()`) on
* Unix-like system. Default value is false.
* @param[in] timestampProvider the source that will provide the timestamp associated to each captured
* packet.
* @param[in] timestampPrecision the precision of the timestamp associated to each captured packet.
*/
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 @@ -264,6 +313,8 @@ namespace pcpp
this->snapshotLength = snapshotLength;
this->nflogGroup = nflogGroup;
this->usePoll = usePoll;
this->timestampProvider = timestampProvider;
this->timestampPrecision = timestampPrecision;
}
};

Expand Down
129 changes: 129 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 @@ -84,6 +89,120 @@ 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)
{
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)
{
int tstampType = timestampProviderMap(timestampProvider);
Copy link
Owner

Choose a reason for hiding this comment

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

nit: you can use auto instead of int

Copy link
Collaborator

Choose a reason for hiding this comment

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

It doesn't actually save you much in this case tho, does it? 🤔

Copy link
Owner

Choose a reason for hiding this comment

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

not really, it's just nicer code 🙂

int* supportedTstampTypes = nullptr;
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.


bool isSupported = false;
if (numSupportedTstampTypes < 0)
{
std::cerr << "Error retrieving timestamp types: " << pcap_geterr(pcap) << " - default Host will be used"
<< std::endl;
isSupported = false;
}
else if (numSupportedTstampTypes == 1)
{
// If 1 is returned, then the only available typestamp 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?

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

pcap_free_tstamp_types(supportedTstampTypes);
return isSupported;
}

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 @@ -308,6 +427,16 @@ namespace pcpp
}
#endif

if (config.timestampProvider != PcapLiveDevice::TimestampProvider::Host)
{
setTimestampProvider(pcap, config.timestampProvider);
}

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

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