From 3cfe407ec0e5802b0e2937f44eb9c5ec2d52a1c1 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Wed, 19 Nov 2025 21:57:24 +0200 Subject: [PATCH 1/3] Update same thread capture code. --- Pcap++/header/PcapLiveDevice.h | 5 -- Pcap++/src/PcapLiveDevice.cpp | 112 +++++++++++++++++++++++---------- 2 files changed, 79 insertions(+), 38 deletions(-) diff --git a/Pcap++/header/PcapLiveDevice.h b/Pcap++/header/PcapLiveDevice.h index 5c26aa4baf..6aa6502ba7 100644 --- a/Pcap++/header/PcapLiveDevice.h +++ b/Pcap++/header/PcapLiveDevice.h @@ -103,8 +103,6 @@ namespace pcpp // Should be set to true by the Callee for the Caller std::atomic m_CaptureThreadStarted; - OnPacketArrivesStopBlocking m_cbOnPacketArrivesBlockingMode; - void* m_cbOnPacketArrivesBlockingModeUserCookie; LinkLayerType m_LinkType; bool m_UsePoll; @@ -119,9 +117,6 @@ namespace pcpp void setDeviceMtu(); void setDeviceMacAddress(); void setDefaultGateway(); - - static void onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet); - public: /// The type of the live device enum LiveDeviceType diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 89f437cf4e..716bf4644b 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -286,6 +286,14 @@ namespace pcpp RawPacketVector* capturedPackets = nullptr; }; + struct CaptureContextWithCancellation + { + PcapLiveDevice* device = nullptr; + OnPacketArrivesStopBlocking callback; + void* userCookie = nullptr; + bool requestStop = false; + }; + // A noop function to be used when no callback is set void onPacketArrivesNoop(uint8_t* user, const pcap_pkthdr* pkthdr, const uint8_t* packet) {} @@ -310,6 +318,46 @@ namespace pcpp context->callback(&rawPacket, context->device, context->userCookie); } + // @brief Wraps the raw packet data into a RawPacket instance and calls the user callback with stop indication + void onPacketArrivesCallbackWithCancellation(uint8_t* user, const pcap_pkthdr* pkthdr, const uint8_t* packet) + { + auto* context = reinterpret_cast(user); + if (context == nullptr || context->device == nullptr || context->callback == nullptr) + { + PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance or callback"); + return; + } + + if (context->requestStop) + { + // If requestStop is true, there is no need to process the packet + PCPP_LOG_DEBUG("Capture request stop is set, skipping packet processing"); + return; + } + + RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, context->device->getLinkType()); + + try + { + if (context->callback(&rawPacket, context->device, context->userCookie)) + { + // If the callback returns true, it means that the user wants to stop the capture + PCPP_LOG_DEBUG("Capture callback requested to stop capturing"); + context->requestStop = true; + } + } + catch (const std::exception& ex) + { + PCPP_LOG_ERROR("Exception occurred while invoking packet arrival callback: " << ex.what()); + context->requestStop = true; // Stop capture on exception + } + catch (...) + { + PCPP_LOG_ERROR("Unknown exception occurred while invoking packet arrival callback"); + context->requestStop = true; // Stop capture on unknown exception + } + } + /// @brief Wraps the raw packet data into a RawPacket instance and adds it to the captured packets vector /// @param user A pointer to an AccumulatorCaptureContext instance /// @param pkthdr A pointer to the pcap_pkthdr struct @@ -429,8 +477,6 @@ namespace pcpp m_CaptureThreadStarted = false; m_StopThread = false; m_CaptureThread = {}; - m_cbOnPacketArrivesBlockingMode = nullptr; - m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; if (calculateMacAddress) { setDeviceMacAddress(); @@ -438,24 +484,6 @@ namespace pcpp } } - void PcapLiveDevice::onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, - const uint8_t* packet) - { - PcapLiveDevice* pThis = reinterpret_cast(user); - if (pThis == nullptr) - { - PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance"); - return; - } - - RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType()); - - if (pThis->m_cbOnPacketArrivesBlockingMode != nullptr) - if (pThis->m_cbOnPacketArrivesBlockingMode(&rawPacket, pThis, - pThis->m_cbOnPacketArrivesBlockingModeUserCookie)) - pThis->m_StopThread = true; - } - internal::PcapHandle PcapLiveDevice::doOpen(const DeviceConfiguration& config) { char errbuf[PCAP_ERRBUF_SIZE] = { '\0' }; @@ -813,9 +841,6 @@ namespace pcpp return 0; } - m_cbOnPacketArrivesBlockingMode = std::move(onPacketArrives); - m_cbOnPacketArrivesBlockingModeUserCookie = userCookie; - m_CaptureThreadStarted = true; m_StopThread = false; @@ -832,17 +857,28 @@ namespace pcpp bool shouldReturnError = false; + CaptureContextWithCancellation context; + context.device = this; + context.callback = std::move(onPacketArrives); + context.userCookie = userCookie; + context.requestStop = false; + if (timeoutMs <= 0) { while (!m_StopThread) { - if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesBlockingMode, - reinterpret_cast(this)) == -1) + if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesCallbackWithCancellation, + reinterpret_cast(&context)) == -1) { PCPP_LOG_ERROR("pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError()); shouldReturnError = true; m_StopThread = true; } + else if (context.requestStop) + { + // If the callback requested to stop the capture, we break the loop + m_StopThread = true; + } } } else @@ -864,13 +900,18 @@ namespace pcpp if (ready > 0) { - if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesBlockingMode, - reinterpret_cast(this)) == -1) + if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesCallbackWithCancellation, + reinterpret_cast(&context)) == -1) { PCPP_LOG_ERROR("pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError()); shouldReturnError = true; m_StopThread = true; } + else if (context.requestStop) + { + // If the callback requested to stop the capture, we break the loop + m_StopThread = true; + } } else if (ready < 0) { @@ -886,13 +927,18 @@ namespace pcpp } else { - if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesBlockingMode, - reinterpret_cast(this)) == -1) + if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesCallbackWithCancellation, + reinterpret_cast(&context)) == -1) { PCPP_LOG_ERROR("pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError()); shouldReturnError = true; m_StopThread = true; } + else if (context.requestStop) + { + // If the callback requested to stop the capture, we break the loop + m_StopThread = true; + } } currentTime = std::chrono::steady_clock::now(); } @@ -900,8 +946,6 @@ namespace pcpp m_CaptureThreadStarted = false; m_StopThread = false; - m_cbOnPacketArrivesBlockingMode = nullptr; - m_cbOnPacketArrivesBlockingModeUserCookie = nullptr; if (shouldReturnError) { @@ -917,9 +961,11 @@ namespace pcpp void PcapLiveDevice::stopCapture() { - // in blocking mode stop capture isn't relevant - if (m_cbOnPacketArrivesBlockingMode != nullptr) + // In blocking mode, there is no capture thread, so we don't need to stop it + if (!m_CaptureThread.joinable()) + { return; + } if (m_CaptureThread.get_id() != std::thread::id{} && m_CaptureThread.get_id() == std::this_thread::get_id()) { From da25f2d89b0b9ad8629ea744c7163764c2cfb8aa Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Wed, 19 Nov 2025 22:18:54 +0200 Subject: [PATCH 2/3] Updated timeout calculations. --- Pcap++/src/PcapLiveDevice.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 716bf4644b..4929f3151b 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -844,8 +844,12 @@ namespace pcpp m_CaptureThreadStarted = true; m_StopThread = false; - const int64_t timeoutMs = timeout * 1000; // timeout unit is seconds, let's change it to milliseconds + // A valid timeout is only generated when timeout is positive. + // This means that the timeout timepoint should be after the start time. + const bool hasTimeout = timeout > 0; auto startTime = std::chrono::steady_clock::now(); + // Calculate the timeout timepoint, cast the double timeout (in seconds) to milliseconds for greater precision + auto timeoutTime = startTime + std::chrono::milliseconds(static_cast(timeout * 1000)); auto currentTime = startTime; #if !defined(_WIN32) @@ -863,7 +867,8 @@ namespace pcpp context.userCookie = userCookie; context.requestStop = false; - if (timeoutMs <= 0) + // No timeout specified, run until stopped + if (!hasTimeout) { while (!m_StopThread) { @@ -883,15 +888,13 @@ namespace pcpp } else { - auto const timeoutTimepoint = startTime + std::chrono::milliseconds(timeoutMs); - - while (!m_StopThread && currentTime < timeoutTimepoint) + while (!m_StopThread && currentTime < timeoutTime) { if (m_UsePoll) { #if !defined(_WIN32) int64_t pollTimeoutMs = - std::chrono::duration_cast(timeoutTimepoint - currentTime).count(); + std::chrono::duration_cast(timeoutTime - currentTime).count(); // poll will be in blocking mode if negative value pollTimeoutMs = std::max(pollTimeoutMs, static_cast(0)); @@ -952,9 +955,10 @@ namespace pcpp return 0; } - if (std::chrono::duration_cast(currentTime - startTime).count() >= timeoutMs) + // Check the time only if a valid timeout was specified. Otherwise it would always be true. + if (hasTimeout && currentTime >= timeoutTime) { - return -1; + return -1; // If we are past the timeout time, return -1 } return 1; } From dc91d05ee611198cc940afb6bd070682ec0eed48 Mon Sep 17 00:00:00 2001 From: Dimitar Krastev Date: Wed, 19 Nov 2025 22:19:22 +0200 Subject: [PATCH 3/3] Lint --- Pcap++/header/PcapLiveDevice.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Pcap++/header/PcapLiveDevice.h b/Pcap++/header/PcapLiveDevice.h index 6aa6502ba7..b68290b8b0 100644 --- a/Pcap++/header/PcapLiveDevice.h +++ b/Pcap++/header/PcapLiveDevice.h @@ -117,6 +117,7 @@ namespace pcpp void setDeviceMtu(); void setDeviceMacAddress(); void setDefaultGateway(); + public: /// The type of the live device enum LiveDeviceType