Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
26 changes: 25 additions & 1 deletion Pcap++/header/PcapLiveDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,22 @@ namespace pcpp
Nanoseconds,
};

/// Controls whether libpcap's "immediate mode" is used for packet delivery.
/// PcapPlusPlus enables immediate mode 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.
enum class BufferingMode
{
/// Let the device decide, currently equivalent to Immediate. Default value.
Default = 0,
/// Use immediate mode, trading throughput/CPU for minimal packet delivery latency
Immediate,
/// Disable 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
Buffered
};

/// @struct DeviceConfiguration
/// A struct that contains user configurable parameters for opening a device. All parameters have default values
/// so the user isn't expected to set all parameters or understand exactly how they work
Expand Down Expand Up @@ -318,6 +334,10 @@ namespace pcpp
/// Depending on the capture device and the software on the host, different precision can be used
TimestampPrecision timestampPrecision;

/// Controls whether libpcap's immediate mode is used. See BufferingMode for details.
/// Default value is BufferingMode::Default.
BufferingMode bufferingMode;

/// 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 +358,14 @@ 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] bufferingMode Controls whether libpcap's immediate mode is used. See BufferingMode for
/// details. Default value is BufferingMode::Default.
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,
BufferingMode bufferingMode = BufferingMode::Default)
{
this->mode = mode;
this->packetBufferTimeoutMs = packetBufferTimeoutMs;
Expand All @@ -353,6 +376,7 @@ namespace pcpp
this->usePoll = usePoll;
this->timestampProvider = timestampProvider;
this->timestampPrecision = timestampPrecision;
this->bufferingMode = bufferingMode;
}
};

Expand Down
24 changes: 20 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,16 @@ 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);
bool useBufferedMode = config.bufferingMode == BufferingMode::Buffered;

int timeout = config.packetBufferTimeoutMs;
if (timeout <= 0)
{
// FreeBSD/macOS already have a positive LIBPCAP_OPEN_LIVE_TIMEOUT for the pcap_breakloop() workaround
bool useNonImmediateModeDefault = useBufferedMode && LIBPCAP_OPEN_LIVE_TIMEOUT <= 0;
// cppcheck-suppress knownConditionTrueFalse
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 +690,13 @@ namespace pcpp
}

#ifdef HAS_PCAP_IMMEDIATE_MODE
ret = pcap_set_immediate_mode(pcap.get(), 1);
if (ret != 0)
if (!useBufferedMode)
{
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().bufferingMode = pcpp::PcapLiveDevice::BufferingMode::Buffered;

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