@@ -272,6 +272,126 @@ namespace pcpp
272272 }
273273 PCPP_LOG_DEBUG (" Ended periodic statistics update procedure" );
274274 }
275+
276+ struct CaptureContext
277+ {
278+ PcapLiveDevice* device = nullptr ;
279+ OnPacketArrivesCallback callback;
280+ void * userCookie = nullptr ;
281+ };
282+
283+ struct AccumulatorCaptureContext
284+ {
285+ PcapLiveDevice* device = nullptr ;
286+ RawPacketVector* capturedPackets = nullptr ;
287+ };
288+
289+ // A noop function to be used when no callback is set
290+ void onPacketArrivesNoop (uint8_t * user, const pcap_pkthdr* pkthdr, const uint8_t * packet)
291+ {}
292+
293+ // @brief Wraps the raw packet data into a RawPacket instance and calls the user callback
294+ // @param user A pointer to a CaptureContext instance
295+ // @param pkthdr A pointer to the pcap_pkthdr struct
296+ // @param packet A pointer to the raw packet data
297+ void onPacketArrivesCallback (uint8_t * user, const pcap_pkthdr* pkthdr, const uint8_t * packet)
298+ {
299+ auto * context = reinterpret_cast <CaptureContext*>(user);
300+ if (context == nullptr || context->device == nullptr || context->callback == nullptr )
301+ {
302+ PCPP_LOG_ERROR (" Unable to extract PcapLiveDevice instance or callback" );
303+ return ;
304+ }
305+
306+ // TODO: Add exception handling to tunnel the exceptions from here to the capture thread
307+ // through the C code boundary
308+
309+ RawPacket rawPacket (packet, pkthdr->caplen , pkthdr->ts , false , context->device ->getLinkType ());
310+ context->callback (&rawPacket, context->device , context->userCookie );
311+ }
312+
313+ // / @brief Wraps the raw packet data into a RawPacket instance and adds it to the captured packets vector
314+ // / @param user A pointer to an AccumulatorCaptureContext instance
315+ // / @param pkthdr A pointer to the pcap_pkthdr struct
316+ // / @param packet A pointer to the raw packet data
317+ void onPacketArrivesAccumulator (uint8_t * user, const pcap_pkthdr* pkthdr, const uint8_t * packet)
318+ {
319+ auto * context = reinterpret_cast <AccumulatorCaptureContext*>(user);
320+ if (context == nullptr || context->device == nullptr || context->capturedPackets == nullptr )
321+ {
322+ PCPP_LOG_ERROR (" Unable to extract PcapLiveDevice instance or captured packets vector" );
323+ return ;
324+ }
325+
326+ // TODO: Add exception handling to tunnel the exceptions from here to the capture thread
327+ // through the C code boundary
328+
329+ uint8_t * packetData = new uint8_t [pkthdr->caplen ];
330+ std::memcpy (packetData, packet, pkthdr->caplen );
331+ auto rawPacket = std::make_unique<RawPacket>(packetData, pkthdr->caplen , pkthdr->ts , true ,
332+ context->device ->getLinkType ());
333+ context->capturedPackets ->pushBack (std::move (rawPacket));
334+ }
335+
336+ // / @brief A procedure that dispatches packets to a user-defined callback function whenever a packet arrives.
337+ // /
338+ // / The procedure runs in a loop until the `stopFlag` is set to true.
339+ // / The procedure will set the `hasStarted` flag to true at the start of the procedure.
340+ // /
341+ // / @param stopFlag A reference to an atomic boolean that indicates when to stop capturing packets
342+ // / @param hasStarted A reference to an atomic boolean that indicates when the capture thread has started
343+ // / @param pcapDescriptor A reference to the pcap handle on which to call pcap_dispatch
344+ // / @param context A CaptureContext instance that holds the device, callback and user cookie.
345+ void captureThreadMain (std::atomic_bool& stopFlag, std::atomic_bool& hasStarted,
346+ internal::PcapHandle const & pcapDescriptor, CaptureContext context)
347+ {
348+ PCPP_LOG_DEBUG (" Started capture thread for device '" << context.device ->getName () << " '" );
349+ hasStarted.store (true );
350+
351+ // If the callback is null, we use a no-op handler to avoid unnecessary overhead
352+ // Statistics only capture still requires pcap_dispatch to be called, but we don't need to process
353+ // packets.
354+ pcap_handler callbackHandler = context.callback ? onPacketArrivesCallback : onPacketArrivesNoop;
355+
356+ while (!stopFlag.load ())
357+ {
358+ if (pcap_dispatch (pcapDescriptor.get (), -1 , callbackHandler, reinterpret_cast <uint8_t *>(&context)) ==
359+ -1 )
360+ {
361+ PCPP_LOG_ERROR (" pcap_dispatch returned an error: " << pcapDescriptor.getLastError ());
362+ stopFlag.store (true );
363+ }
364+ }
365+
366+ PCPP_LOG_DEBUG (" Ended capture thread for device '" << context.device ->getName () << " '" );
367+ }
368+
369+ // / @brief A procedure that accumulates captured packets into a vector.
370+ // /
371+ // / The procedure runs in a loop until the `stopFlag` is set to true.
372+ // / The procedure will set the `hasStarted` flag to true at the start of the procedure.
373+ // /
374+ // / @param stopFlag A reference to an atomic boolean that indicates when to stop capturing packets
375+ // / @param hasStarted A reference to an atomic boolean that indicates when the capture thread has started
376+ // / @param pcapDescriptor A reference to the pcap handle on which to call pcap_dispatch
377+ // / @param context An AccumulatorCaptureContext instance that holds the device and the captured packets vector.
378+ void captureThreadMainAccumulator (std::atomic_bool& stopFlag, std::atomic_bool& hasStarted,
379+ internal::PcapHandle const & pcapDescriptor, AccumulatorCaptureContext context)
380+ {
381+ PCPP_LOG_DEBUG (" Started capture thread for device '" << context.device ->getName () << " '" );
382+ hasStarted.store (true );
383+ while (!stopFlag.load ())
384+ {
385+ if (pcap_dispatch (pcapDescriptor.get (), 100 , onPacketArrivesAccumulator,
386+ reinterpret_cast <uint8_t *>(&context)) == -1 )
387+ {
388+ PCPP_LOG_ERROR (" pcap_dispatch returned an error: " << pcapDescriptor.getLastError ());
389+ stopFlag.store (true );
390+ }
391+ }
392+
393+ PCPP_LOG_DEBUG (" Ended capture thread for device '" << context.device ->getName () << " '" );
394+ }
275395 } // namespace
276396
277397 PcapLiveDevice::PcapLiveDevice (DeviceInterfaceDetails interfaceDetails, bool calculateMTU, bool calculateMacAddress,
@@ -309,50 +429,15 @@ namespace pcpp
309429 m_CaptureThreadStarted = false ;
310430 m_StopThread = false ;
311431 m_CaptureThread = {};
312- m_cbOnPacketArrives = nullptr ;
313432 m_cbOnPacketArrivesBlockingMode = nullptr ;
314433 m_cbOnPacketArrivesBlockingModeUserCookie = nullptr ;
315- m_cbOnPacketArrivesUserCookie = nullptr ;
316- m_CaptureCallbackMode = true ;
317- m_CapturedPackets = nullptr ;
318434 if (calculateMacAddress)
319435 {
320436 setDeviceMacAddress ();
321437 PCPP_LOG_DEBUG (" MAC addr: " << m_MacAddress);
322438 }
323439 }
324440
325- void PcapLiveDevice::onPacketArrives (uint8_t * user, const struct pcap_pkthdr * pkthdr, const uint8_t * packet)
326- {
327- PcapLiveDevice* pThis = reinterpret_cast <PcapLiveDevice*>(user);
328- if (pThis == nullptr )
329- {
330- PCPP_LOG_ERROR (" Unable to extract PcapLiveDevice instance" );
331- return ;
332- }
333-
334- RawPacket rawPacket (packet, pkthdr->caplen , pkthdr->ts , false , pThis->getLinkType ());
335-
336- if (pThis->m_cbOnPacketArrives != nullptr )
337- pThis->m_cbOnPacketArrives (&rawPacket, pThis, pThis->m_cbOnPacketArrivesUserCookie );
338- }
339-
340- void PcapLiveDevice::onPacketArrivesNoCallback (uint8_t * user, const struct pcap_pkthdr * pkthdr,
341- const uint8_t * packet)
342- {
343- PcapLiveDevice* pThis = reinterpret_cast <PcapLiveDevice*>(user);
344- if (pThis == nullptr )
345- {
346- PCPP_LOG_ERROR (" Unable to extract PcapLiveDevice instance" );
347- return ;
348- }
349-
350- uint8_t * packetData = new uint8_t [pkthdr->caplen ];
351- memcpy (packetData, packet, pkthdr->caplen );
352- RawPacket* rawPacketPtr = new RawPacket (packetData, pkthdr->caplen , pkthdr->ts , true , pThis->getLinkType ());
353- pThis->m_CapturedPackets ->pushBack (rawPacketPtr);
354- }
355-
356441 void PcapLiveDevice::onPacketArrivesBlockingMode (uint8_t * user, const struct pcap_pkthdr * pkthdr,
357442 const uint8_t * packet)
358443 {
@@ -371,37 +456,6 @@ namespace pcpp
371456 pThis->m_StopThread = true ;
372457 }
373458
374- void PcapLiveDevice::captureThreadMain ()
375- {
376- PCPP_LOG_DEBUG (" Started capture thread for device '" << m_InterfaceDetails.name << " '" );
377- m_CaptureThreadStarted = true ;
378-
379- if (m_CaptureCallbackMode)
380- {
381- while (!m_StopThread)
382- {
383- if (pcap_dispatch (m_PcapDescriptor.get (), -1 , onPacketArrives, reinterpret_cast <uint8_t *>(this )) == -1 )
384- {
385- PCPP_LOG_ERROR (" pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError ());
386- m_StopThread = true ;
387- }
388- }
389- }
390- else
391- {
392- while (!m_StopThread)
393- {
394- if (pcap_dispatch (m_PcapDescriptor.get (), 100 , onPacketArrivesNoCallback,
395- reinterpret_cast <uint8_t *>(this )) == -1 )
396- {
397- PCPP_LOG_ERROR (" pcap_dispatch returned an error: " << m_PcapDescriptor.getLastError ());
398- m_StopThread = true ;
399- }
400- }
401- }
402- PCPP_LOG_DEBUG (" Ended capture thread for device '" << m_InterfaceDetails.name << " '" );
403- }
404-
405459 internal::PcapHandle PcapLiveDevice::doOpen (const DeviceConfiguration& config)
406460 {
407461 char errbuf[PCAP_ERRBUF_SIZE] = { ' \0 ' };
@@ -654,11 +708,13 @@ namespace pcpp
654708 return false ;
655709 }
656710
657- m_CaptureCallbackMode = true ;
658- m_cbOnPacketArrives = std::move (onPacketArrives);
659- m_cbOnPacketArrivesUserCookie = onPacketArrivesUserCookie;
711+ CaptureContext context;
712+ context.device = this ;
713+ context.callback = std::move (onPacketArrives);
714+ context.userCookie = onPacketArrivesUserCookie;
660715
661- m_CaptureThread = std::thread (&pcpp::PcapLiveDevice::captureThreadMain, this );
716+ m_CaptureThread = std::thread (&captureThreadMain, std::ref (m_StopThread), std::ref (m_CaptureThreadStarted),
717+ std::ref (m_PcapDescriptor), std::move (context));
662718
663719 // Wait thread to be start
664720 // C++20 = m_CaptureThreadStarted.wait(true);
@@ -710,11 +766,15 @@ namespace pcpp
710766 return false ;
711767 }
712768
713- m_CapturedPackets = &capturedPacketsVector;
714- m_CapturedPackets->clear ();
769+ capturedPacketsVector.clear ();
770+
771+ AccumulatorCaptureContext context;
772+ context.device = this ;
773+ context.capturedPackets = &capturedPacketsVector;
774+
775+ m_CaptureThread = std::thread (&captureThreadMainAccumulator, std::ref (m_StopThread),
776+ std::ref (m_CaptureThreadStarted), std::ref (m_PcapDescriptor), std::move (context));
715777
716- m_CaptureCallbackMode = false ;
717- m_CaptureThread = std::thread (&pcpp::PcapLiveDevice::captureThreadMain, this );
718778 // Wait thread to be start
719779 // C++20 = m_CaptureThreadStarted.wait(true);
720780 while (m_CaptureThreadStarted != true )
@@ -752,8 +812,6 @@ namespace pcpp
752812 PCPP_LOG_ERROR (" Failed to prepare capture: " << ex.what ());
753813 return 0 ;
754814 }
755- m_cbOnPacketArrives = nullptr ;
756- m_cbOnPacketArrivesUserCookie = nullptr ;
757815
758816 m_cbOnPacketArrivesBlockingMode = std::move (onPacketArrives);
759817 m_cbOnPacketArrivesBlockingModeUserCookie = userCookie;
0 commit comments