Skip to content

Commit 4e33e12

Browse files
authored
Cleanup PcapLiveDevice synchronous capture. (#2026)
* Update same thread capture code. * Updated timeout calculations. * Lint
1 parent bdec00b commit 4e33e12

File tree

2 files changed

+91
-45
lines changed

2 files changed

+91
-45
lines changed

Pcap++/header/PcapLiveDevice.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ namespace pcpp
103103
// Should be set to true by the Callee for the Caller
104104
std::atomic<bool> m_CaptureThreadStarted;
105105

106-
OnPacketArrivesStopBlocking m_cbOnPacketArrivesBlockingMode;
107-
void* m_cbOnPacketArrivesBlockingModeUserCookie;
108106
LinkLayerType m_LinkType;
109107
bool m_UsePoll;
110108

@@ -120,8 +118,6 @@ namespace pcpp
120118
void setDeviceMacAddress();
121119
void setDefaultGateway();
122120

123-
static void onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr, const uint8_t* packet);
124-
125121
public:
126122
/// The type of the live device
127123
enum LiveDeviceType

Pcap++/src/PcapLiveDevice.cpp

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ namespace pcpp
286286
RawPacketVector* capturedPackets = nullptr;
287287
};
288288

289+
struct CaptureContextWithCancellation
290+
{
291+
PcapLiveDevice* device = nullptr;
292+
OnPacketArrivesStopBlocking callback;
293+
void* userCookie = nullptr;
294+
bool requestStop = false;
295+
};
296+
289297
// A noop function to be used when no callback is set
290298
void onPacketArrivesNoop(uint8_t* user, const pcap_pkthdr* pkthdr, const uint8_t* packet)
291299
{}
@@ -310,6 +318,46 @@ namespace pcpp
310318
context->callback(&rawPacket, context->device, context->userCookie);
311319
}
312320

321+
// @brief Wraps the raw packet data into a RawPacket instance and calls the user callback with stop indication
322+
void onPacketArrivesCallbackWithCancellation(uint8_t* user, const pcap_pkthdr* pkthdr, const uint8_t* packet)
323+
{
324+
auto* context = reinterpret_cast<CaptureContextWithCancellation*>(user);
325+
if (context == nullptr || context->device == nullptr || context->callback == nullptr)
326+
{
327+
PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance or callback");
328+
return;
329+
}
330+
331+
if (context->requestStop)
332+
{
333+
// If requestStop is true, there is no need to process the packet
334+
PCPP_LOG_DEBUG("Capture request stop is set, skipping packet processing");
335+
return;
336+
}
337+
338+
RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, context->device->getLinkType());
339+
340+
try
341+
{
342+
if (context->callback(&rawPacket, context->device, context->userCookie))
343+
{
344+
// If the callback returns true, it means that the user wants to stop the capture
345+
PCPP_LOG_DEBUG("Capture callback requested to stop capturing");
346+
context->requestStop = true;
347+
}
348+
}
349+
catch (const std::exception& ex)
350+
{
351+
PCPP_LOG_ERROR("Exception occurred while invoking packet arrival callback: " << ex.what());
352+
context->requestStop = true; // Stop capture on exception
353+
}
354+
catch (...)
355+
{
356+
PCPP_LOG_ERROR("Unknown exception occurred while invoking packet arrival callback");
357+
context->requestStop = true; // Stop capture on unknown exception
358+
}
359+
}
360+
313361
/// @brief Wraps the raw packet data into a RawPacket instance and adds it to the captured packets vector
314362
/// @param user A pointer to an AccumulatorCaptureContext instance
315363
/// @param pkthdr A pointer to the pcap_pkthdr struct
@@ -429,33 +477,13 @@ namespace pcpp
429477
m_CaptureThreadStarted = false;
430478
m_StopThread = false;
431479
m_CaptureThread = {};
432-
m_cbOnPacketArrivesBlockingMode = nullptr;
433-
m_cbOnPacketArrivesBlockingModeUserCookie = nullptr;
434480
if (calculateMacAddress)
435481
{
436482
setDeviceMacAddress();
437483
PCPP_LOG_DEBUG(" MAC addr: " << m_MacAddress);
438484
}
439485
}
440486

441-
void PcapLiveDevice::onPacketArrivesBlockingMode(uint8_t* user, const struct pcap_pkthdr* pkthdr,
442-
const uint8_t* packet)
443-
{
444-
PcapLiveDevice* pThis = reinterpret_cast<PcapLiveDevice*>(user);
445-
if (pThis == nullptr)
446-
{
447-
PCPP_LOG_ERROR("Unable to extract PcapLiveDevice instance");
448-
return;
449-
}
450-
451-
RawPacket rawPacket(packet, pkthdr->caplen, pkthdr->ts, false, pThis->getLinkType());
452-
453-
if (pThis->m_cbOnPacketArrivesBlockingMode != nullptr)
454-
if (pThis->m_cbOnPacketArrivesBlockingMode(&rawPacket, pThis,
455-
pThis->m_cbOnPacketArrivesBlockingModeUserCookie))
456-
pThis->m_StopThread = true;
457-
}
458-
459487
internal::PcapHandle PcapLiveDevice::doOpen(const DeviceConfiguration& config)
460488
{
461489
char errbuf[PCAP_ERRBUF_SIZE] = { '\0' };
@@ -813,14 +841,15 @@ namespace pcpp
813841
return 0;
814842
}
815843

816-
m_cbOnPacketArrivesBlockingMode = std::move(onPacketArrives);
817-
m_cbOnPacketArrivesBlockingModeUserCookie = userCookie;
818-
819844
m_CaptureThreadStarted = true;
820845
m_StopThread = false;
821846

822-
const int64_t timeoutMs = timeout * 1000; // timeout unit is seconds, let's change it to milliseconds
847+
// A valid timeout is only generated when timeout is positive.
848+
// This means that the timeout timepoint should be after the start time.
849+
const bool hasTimeout = timeout > 0;
823850
auto startTime = std::chrono::steady_clock::now();
851+
// Calculate the timeout timepoint, cast the double timeout (in seconds) to milliseconds for greater precision
852+
auto timeoutTime = startTime + std::chrono::milliseconds(static_cast<int64_t>(timeout * 1000));
824853
auto currentTime = startTime;
825854

826855
#if !defined(_WIN32)
@@ -832,30 +861,40 @@ namespace pcpp
832861

833862
bool shouldReturnError = false;
834863

835-
if (timeoutMs <= 0)
864+
CaptureContextWithCancellation context;
865+
context.device = this;
866+
context.callback = std::move(onPacketArrives);
867+
context.userCookie = userCookie;
868+
context.requestStop = false;
869+
870+
// No timeout specified, run until stopped
871+
if (!hasTimeout)
836872
{
837873
while (!m_StopThread)
838874
{
839-
if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesBlockingMode,
840-
reinterpret_cast<uint8_t*>(this)) == -1)
875+
if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesCallbackWithCancellation,
876+
reinterpret_cast<uint8_t*>(&context)) == -1)
841877
{
842878
PCPP_LOG_ERROR("pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError());
843879
shouldReturnError = true;
844880
m_StopThread = true;
845881
}
882+
else if (context.requestStop)
883+
{
884+
// If the callback requested to stop the capture, we break the loop
885+
m_StopThread = true;
886+
}
846887
}
847888
}
848889
else
849890
{
850-
auto const timeoutTimepoint = startTime + std::chrono::milliseconds(timeoutMs);
851-
852-
while (!m_StopThread && currentTime < timeoutTimepoint)
891+
while (!m_StopThread && currentTime < timeoutTime)
853892
{
854893
if (m_UsePoll)
855894
{
856895
#if !defined(_WIN32)
857896
int64_t pollTimeoutMs =
858-
std::chrono::duration_cast<std::chrono::milliseconds>(timeoutTimepoint - currentTime).count();
897+
std::chrono::duration_cast<std::chrono::milliseconds>(timeoutTime - currentTime).count();
859898

860899
// poll will be in blocking mode if negative value
861900
pollTimeoutMs = std::max(pollTimeoutMs, static_cast<int64_t>(0));
@@ -864,13 +903,18 @@ namespace pcpp
864903

865904
if (ready > 0)
866905
{
867-
if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesBlockingMode,
868-
reinterpret_cast<uint8_t*>(this)) == -1)
906+
if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesCallbackWithCancellation,
907+
reinterpret_cast<uint8_t*>(&context)) == -1)
869908
{
870909
PCPP_LOG_ERROR("pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError());
871910
shouldReturnError = true;
872911
m_StopThread = true;
873912
}
913+
else if (context.requestStop)
914+
{
915+
// If the callback requested to stop the capture, we break the loop
916+
m_StopThread = true;
917+
}
874918
}
875919
else if (ready < 0)
876920
{
@@ -886,40 +930,46 @@ namespace pcpp
886930
}
887931
else
888932
{
889-
if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesBlockingMode,
890-
reinterpret_cast<uint8_t*>(this)) == -1)
933+
if (pcap_dispatch(m_PcapDescriptor.get(), -1, onPacketArrivesCallbackWithCancellation,
934+
reinterpret_cast<uint8_t*>(&context)) == -1)
891935
{
892936
PCPP_LOG_ERROR("pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError());
893937
shouldReturnError = true;
894938
m_StopThread = true;
895939
}
940+
else if (context.requestStop)
941+
{
942+
// If the callback requested to stop the capture, we break the loop
943+
m_StopThread = true;
944+
}
896945
}
897946
currentTime = std::chrono::steady_clock::now();
898947
}
899948
}
900949

901950
m_CaptureThreadStarted = false;
902951
m_StopThread = false;
903-
m_cbOnPacketArrivesBlockingMode = nullptr;
904-
m_cbOnPacketArrivesBlockingModeUserCookie = nullptr;
905952

906953
if (shouldReturnError)
907954
{
908955
return 0;
909956
}
910957

911-
if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count() >= timeoutMs)
958+
// Check the time only if a valid timeout was specified. Otherwise it would always be true.
959+
if (hasTimeout && currentTime >= timeoutTime)
912960
{
913-
return -1;
961+
return -1; // If we are past the timeout time, return -1
914962
}
915963
return 1;
916964
}
917965

918966
void PcapLiveDevice::stopCapture()
919967
{
920-
// in blocking mode stop capture isn't relevant
921-
if (m_cbOnPacketArrivesBlockingMode != nullptr)
968+
// In blocking mode, there is no capture thread, so we don't need to stop it
969+
if (!m_CaptureThread.joinable())
970+
{
922971
return;
972+
}
923973

924974
if (m_CaptureThread.get_id() != std::thread::id{} && m_CaptureThread.get_id() == std::this_thread::get_id())
925975
{

0 commit comments

Comments
 (0)