Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
16 changes: 15 additions & 1 deletion Pcap++/header/PcapLiveDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ namespace pcpp
/// Depending on the capture device and the software on the host, different precision can be used
TimestampPrecision timestampPrecision;

/// Disable libpcap's "immediate mode", which PcapPlusPlus enables by default (where supported) for
/// minimal packet delivery latency. On Linux, immediate mode also forces a less efficient capture
/// path (TPACKET_V2 with non-blocking polling, instead of TPACKET_V3 with blocking, batched reads),
/// which can hurt throughput and CPU usage at high packet rates. Setting this to true trades some
/// added latency (up to packetBufferTimeoutMs, or 100 ms on Linux/Windows if not set) for those
/// throughput and CPU benefits.
/// Default value is false.
bool disableImmediateMode;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

IMO, positive flags are easier to reason about than negative flags since the reader does not have to negate the condition. useImmediateMode defaulted to true might be easier to reason about when looking through the docs.

Also, I am wondering, do you think it would be better to have the flag be an enum instead of a bool?

Something like:

enum class BufferingMode
{
  Default, // let the device decide (current behavior)
  Immediate, // use immediate mode
  Buffered // use buffered mode
}

Default mode can also possibly have a graceful fallback to buffered, if immediate mode fails to enable for some reason.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah good idea, Ill change


/// 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 @@ -338,11 +347,15 @@ namespace pcpp
/// for each packet (not all platforms support this). Default provider is Host.
/// @param[in] timestampPrecision The timestamp precision (not all platforms support this).
/// Default precision is Microseconds.
/// @param[in] disableImmediateMode Disable libpcap's immediate mode, trading some added latency (up
/// to packetBufferTimeoutMs, or 100 ms on Linux/Windows if not set) for the throughput and CPU
/// benefits described above. Default value is false (immediate mode enabled).
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,
TimestampProvider timestampProvider = TimestampProvider::Host,
TimestampPrecision timestampPrecision = TimestampPrecision::Microseconds)
TimestampPrecision timestampPrecision = TimestampPrecision::Microseconds,
bool disableImmediateMode = false)
{
this->mode = mode;
this->packetBufferTimeoutMs = packetBufferTimeoutMs;
Expand All @@ -353,6 +366,7 @@ namespace pcpp
this->usePoll = usePoll;
this->timestampProvider = timestampProvider;
this->timestampPrecision = timestampPrecision;
this->disableImmediateMode = disableImmediateMode;
}
};

Expand Down
22 changes: 18 additions & 4 deletions Pcap++/src/PcapLiveDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
# define LIBPCAP_OPEN_LIVE_TIMEOUT -1
#endif

// Fallback used on Linux/Windows (where LIBPCAP_OPEN_LIVE_TIMEOUT is -1) when immediate mode is disabled and
// packetBufferTimeoutMs isn't set, since libpcap's timeout behavior is unpredictable without immediate mode
static const int NON_IMMEDIATE_MODE_DEFAULT_TIMEOUT = 100;

static const char* NFLOG_IFACE = "nflog";
static const int DEFAULT_SNAPLEN = 9000;

Expand Down Expand Up @@ -660,7 +664,13 @@ namespace pcpp
throw std::runtime_error("Cannot set promiscuous mode, error was: " + std::string(pcap.getLastError()));
}

int timeout = (config.packetBufferTimeoutMs <= 0 ? LIBPCAP_OPEN_LIVE_TIMEOUT : config.packetBufferTimeoutMs);
int timeout = config.packetBufferTimeoutMs;
if (timeout <= 0)
{
// FreeBSD/macOS already have a positive LIBPCAP_OPEN_LIVE_TIMEOUT for the pcap_breakloop() workaround
bool useNonImmediateModeDefault = config.disableImmediateMode && LIBPCAP_OPEN_LIVE_TIMEOUT <= 0;
timeout = useNonImmediateModeDefault ? NON_IMMEDIATE_MODE_DEFAULT_TIMEOUT : LIBPCAP_OPEN_LIVE_TIMEOUT;
}
ret = pcap_set_timeout(pcap.get(), timeout);
if (ret != 0)
{
Expand All @@ -677,10 +687,14 @@ namespace pcpp
}

#ifdef HAS_PCAP_IMMEDIATE_MODE
ret = pcap_set_immediate_mode(pcap.get(), 1);
if (ret != 0)
if (!config.disableImmediateMode)
{
throw std::runtime_error("Cannot set immediate mode, error was: " + std::string(pcap.getLastError()));
ret = pcap_set_immediate_mode(pcap.get(), 1);
if (ret != 0)
{
throw std::runtime_error("Cannot set immediate mode, error was: " +
std::string(pcap.getLastError()));
}
}
#endif

Expand Down
3 changes: 3 additions & 0 deletions Tests/Pcap++Test/Tests/LiveDeviceTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,9 @@ PTF_TEST_CASE(TestPcapLiveDeviceBlockingMode)
configs[1].usePoll = true;
# endif

configs.emplace_back(); // disables immediate mode so libpcap can use TPACKET_V3 batching on Linux
configs.back().disableImmediateMode = true;

// test the common behaviour for all configs
for (const auto& config : configs)
{
Expand Down
Loading