From 01957ac534c2015bab852db40d44c4b1fdd7a0d3 Mon Sep 17 00:00:00 2001 From: "vincenzo.comito" Date: Tue, 10 Dec 2024 09:42:59 +0100 Subject: [PATCH] Add support for timestamp related options (#1656) --- Pcap++/header/PcapLiveDevice.h | 53 +++++++++++++- Pcap++/src/PcapLiveDevice.cpp | 129 +++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/Pcap++/header/PcapLiveDevice.h b/Pcap++/header/PcapLiveDevice.h index e37e27d7cd..561e025ce9 100644 --- a/Pcap++/header/PcapLiveDevice.h +++ b/Pcap++/header/PcapLiveDevice.h @@ -186,6 +186,38 @@ namespace pcpp PCPP_OUT }; + /** + * Set which source provides timestamps associated to each captured packet + * (you can read more here: ) + */ + 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: ) + */ + 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 @@ -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 @@ -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; @@ -264,6 +313,8 @@ namespace pcpp this->snapshotLength = snapshotLength; this->nflogGroup = nflogGroup; this->usePoll = usePoll; + this->timestampProvider = timestampProvider; + this->timestampPrecision = timestampPrecision; } }; diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 6eef2111d2..a877f1686a 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -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 { @@ -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); + int* supportedTstampTypes = nullptr; + const int numSupportedTstampTypes = pcap_list_tstamp_types(pcap, &supportedTstampTypes); + + 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; + } + 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) { @@ -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) {