Skip to content

Commit 2a39a25

Browse files
authored
Make libpcap / WinPcap / Npcap optional (#2057)
1 parent 27830b3 commit 2a39a25

15 files changed

Lines changed: 267 additions & 77 deletions

File tree

.github/workflows/build_and_test.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,67 @@ jobs:
246246
path: ${{ env.CCACHE_DIR }}
247247
key: ${{ steps.ccache-restore.outputs.cache-primary-key }}
248248

249+
libpcap-free:
250+
runs-on: ubuntu-latest
251+
container: seladb/ubuntu2404
252+
env:
253+
image: ubuntu2404
254+
steps:
255+
- name: Checkout code
256+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
257+
258+
# Checkout is performed out of the container and doesn't match our user
259+
- name: Fix checkout ownership
260+
run: chown -R "$(id -u):$(id -g)" "$GITHUB_WORKSPACE"
261+
262+
- name: Restore Ccache
263+
id: ccache-restore
264+
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
265+
with:
266+
path: |
267+
${{ env.CCACHE_DIR }}
268+
!*.gcda
269+
!*.gcno
270+
key: ${{ env.image }}-ccache-${{ github.run_id }}
271+
restore-keys: |
272+
${{ env.image }}-ccache
273+
274+
- name: Configure PcapPlusPlus
275+
run: cmake -DPCAPPP_BUILD_COVERAGE=ON -DPCAPPP_USE_PCAP=OFF -S . -B "$BUILD_DIR"
276+
277+
- name: Build PcapPlusPlus
278+
run: cmake --build "$BUILD_DIR" -j
279+
280+
- name: Prepare environment for tests
281+
run: |
282+
python3 -m venv .venv
283+
. .venv/bin/activate
284+
python3 -m pip install -r ci/run_tests/requirements.txt
285+
286+
- name: Test PcapPlusPlus
287+
run: |
288+
. .venv/bin/activate
289+
python3 ci/run_tests/run_tests.py --interface eth0
290+
291+
- name: Check installation
292+
run: |
293+
cmake -DPCAPPP_BUILD_COVERAGE=OFF -S . -B "$BUILD_DIR"
294+
cmake --build "$BUILD_DIR" -j
295+
cmake --install "$BUILD_DIR"
296+
297+
- name: Test Examples
298+
run: |
299+
. .venv/bin/activate
300+
cd Tests/ExamplesTest
301+
python3 -m pip install -r requirements.txt
302+
python3 -m pytest --interface eth0 --root-path=../../Dist/examples_bin -m no_pcap
303+
304+
- name: Save Ccache
305+
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
306+
with:
307+
path: ${{ env.CCACHE_DIR }}
308+
key: ${{ steps.ccache-restore.outputs.cache-primary-key }}
309+
249310
dpdk:
250311
runs-on: ubuntu-latest
251312
container: seladb/${{ matrix.image }}

CMakeLists.txt

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ cmake_dependent_option(
163163
"PCAPPP_USE_DPDK"
164164
OFF
165165
)
166+
option(PCAPPP_USE_PCAP "Setup PcapPlusPlus with libpcap / WinPcap / Npcap" ON)
166167
option(PCAPPP_USE_PF_RING "Setup PcapPlusPlus with PF_RING. In this case you must also set PF_RING_ROOT")
167168
option(PCAPPP_USE_XDP "Setup PcapPlusPlus with XDP")
168169
option(PCAPPP_USE_WINDIVERT "Setup PcapPlusPlus with WinDivert")
@@ -210,26 +211,30 @@ endif()
210211

211212
# Required dependencies only for PCap++ project.
212213
if(PCAPPP_BUILD_PCAPPP)
213-
# Usually on Windows PCAP_ROOT and Packet_ROOT are at the same location
214-
if(WIN32 AND PCAP_ROOT AND NOT Packet_ROOT)
215-
set(Packet_ROOT ${PCAP_ROOT})
216-
endif()
214+
# PCAP/Npcap/WinPcap is optional and controlled by PCAPPP_USE_PCAP
215+
if(PCAPPP_USE_PCAP)
216+
# Usually on Windows PCAP_ROOT and Packet_ROOT are at the same location
217+
if(WIN32 AND PCAP_ROOT AND NOT Packet_ROOT)
218+
set(Packet_ROOT ${PCAP_ROOT})
219+
endif()
217220

218-
find_package(PCAP)
219-
if(NOT PCAP_FOUND)
220-
if(WIN32)
221-
message(FATAL_ERROR "Please specify Npcap/WinPcap SDK directory with -DPCAP_ROOT=<PCAP_SDK_PATH>")
222-
else()
223-
message(FATAL_ERROR "PCAP library not found!")
221+
find_package(PCAP)
222+
if(NOT PCAP_FOUND)
223+
if(WIN32)
224+
message(FATAL_ERROR "Please specify Npcap/WinPcap SDK directory with -DPCAP_ROOT=<PCAP_SDK_PATH>")
225+
else()
226+
message(FATAL_ERROR "PCAP library not found!")
227+
endif()
224228
endif()
225-
endif()
226229

227-
# Look for Packet
228-
if(WIN32)
229-
find_package(Packet)
230-
if(NOT Packet_FOUND)
231-
message(FATAL_ERROR "Please specify Packet library -DPacket_ROOT=<NPCAP_SDK_PATH>")
230+
# Look for Packet
231+
if(WIN32)
232+
find_package(Packet)
233+
if(NOT Packet_FOUND)
234+
message(FATAL_ERROR "Please specify Packet library -DPacket_ROOT=<NPCAP_SDK_PATH>")
235+
endif()
232236
endif()
237+
add_compile_definitions(USE_PCAP)
233238
endif()
234239

235240
if(HAVE_PCAP_IMMEDIATE_MODE)
@@ -379,10 +384,12 @@ if(PCAPPP_INSTALL)
379384
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PcapPlusPlus.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
380385
endif()
381386

382-
# CMake helpers to compile Pcap++ with CMake
383-
pcapp_install_cmake_module(PCAP)
384-
if(WIN32)
385-
pcapp_install_cmake_module(Packet)
387+
# CMake helpers to compile Pcap++ with CMake (only if PCAP is used)
388+
if(PCAPPP_USE_PCAP)
389+
pcapp_install_cmake_module(PCAP)
390+
if(WIN32)
391+
pcapp_install_cmake_module(Packet)
392+
endif()
386393
endif()
387394

388395
if(PCAPPP_USE_PF_RING)

Examples/CMakeLists.txt

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,24 @@ else()
2020
message(STATUS "Include dir: ${PcapPlusPlus_INCLUDE_DIR}")
2121
endif()
2222

23-
add_subdirectory(Arping)
24-
add_subdirectory(ArpSpoofing)
25-
add_subdirectory(DNSResolver)
26-
add_subdirectory(DnsSpoofing)
27-
add_subdirectory(HttpAnalyzer)
28-
add_subdirectory(IcmpFileTransfer)
29-
add_subdirectory(IPDefragUtil)
30-
add_subdirectory(IPFragUtil)
31-
add_subdirectory(PcapPlusPlus-benchmark)
3223
add_subdirectory(PcapPrinter)
3324
add_subdirectory(PcapSearch)
3425
add_subdirectory(PcapSplitter)
35-
add_subdirectory(SSLAnalyzer)
36-
add_subdirectory(TcpReassembly)
37-
add_subdirectory(TLSFingerprinting)
3826
add_subdirectory(X509Toolkit)
3927

40-
if(PCAPPP_BUILD_TUTORIALS)
41-
set(PCAPPP_BINARY_TUTORIAL_DIR ${CMAKE_BINARY_DIR}/tutorials_bin)
42-
add_subdirectory(Tutorials)
28+
if(PCAPPP_USE_PCAP)
29+
add_subdirectory(Arping)
30+
add_subdirectory(ArpSpoofing)
31+
add_subdirectory(DNSResolver)
32+
add_subdirectory(DnsSpoofing)
33+
add_subdirectory(HttpAnalyzer)
34+
add_subdirectory(IcmpFileTransfer)
35+
add_subdirectory(IPDefragUtil)
36+
add_subdirectory(IPFragUtil)
37+
add_subdirectory(PcapPlusPlus-benchmark)
38+
add_subdirectory(SSLAnalyzer)
39+
add_subdirectory(TcpReassembly)
40+
add_subdirectory(TLSFingerprinting)
4341
endif()
4442

4543
if(PCAPPP_USE_DPDK)
@@ -57,3 +55,8 @@ endif()
5755
if(PCAPPP_USE_XDP)
5856
add_subdirectory(XdpExample-FilterTraffic)
5957
endif()
58+
59+
if(PCAPPP_BUILD_TUTORIALS)
60+
set(PCAPPP_BINARY_TUTORIAL_DIR ${CMAKE_BINARY_DIR}/tutorials_bin)
61+
add_subdirectory(Tutorials)
62+
endif()

Pcap++/CMakeLists.txt

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
add_library(
22
Pcap++
3-
src/DeviceUtils.cpp
3+
$<$<BOOL:${PCAPPP_USE_PCAP}>:src/DeviceUtils.cpp>
44
$<$<BOOL:${PCAPPP_USE_DPDK}>:src/DpdkDevice.cpp>
55
$<$<BOOL:${PCAPPP_USE_DPDK}>:src/DpdkDeviceList.cpp>
66
$<$<BOOL:${PCAPPP_USE_DPDK_KNI}>:src/KniDevice.cpp>
77
$<$<BOOL:${PCAPPP_USE_DPDK_KNI}>:src/KniDeviceList.cpp>
88
$<$<BOOL:${LINUX}>:src/LinuxNicInformationSocket.cpp>
99
$<$<BOOL:${PCAPPP_USE_DPDK}>:src/MBufRawPacket.cpp>
10-
src/PcapUtils.cpp
11-
src/NetworkUtils.cpp
10+
$<$<BOOL:${PCAPPP_USE_PCAP}>:src/PcapUtils.cpp>
11+
$<$<BOOL:${PCAPPP_USE_PCAP}>:src/NetworkUtils.cpp>
1212
src/PcapFileDevice.cpp
13-
src/PcapDevice.cpp
13+
$<$<BOOL:${PCAPPP_USE_PCAP}>:src/PcapDevice.cpp>
1414
src/PcapFilter.cpp
15-
src/PcapLiveDevice.cpp
16-
src/PcapLiveDeviceList.cpp
17-
$<$<BOOL:${WIN32}>:src/PcapRemoteDevice.cpp>
18-
$<$<BOOL:${WIN32}>:src/PcapRemoteDeviceList.cpp>
15+
$<$<BOOL:${PCAPPP_USE_PCAP}>:src/PcapLiveDevice.cpp>
16+
$<$<BOOL:${PCAPPP_USE_PCAP}>:src/PcapLiveDeviceList.cpp>
17+
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${PCAPPP_USE_PCAP}>>:src/PcapRemoteDevice.cpp>
18+
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${PCAPPP_USE_PCAP}>>:src/PcapRemoteDeviceList.cpp>
1919
$<$<BOOL:${PCAPPP_USE_PF_RING}>:src/PfRingDevice.cpp>
2020
$<$<BOOL:${PCAPPP_USE_PF_RING}>:src/PfRingDeviceList.cpp>
2121
$<$<BOOL:${PCAPPP_USE_XDP}>:src/XdpDevice.cpp>
2222
src/RawSocketDevice.cpp
23-
$<$<BOOL:${WIN32}>:src/WinPcapLiveDevice.cpp>
23+
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${PCAPPP_USE_PCAP}>>:src/WinPcapLiveDevice.cpp>
2424
$<$<BOOL:${PCAPPP_USE_WINDIVERT}>:src/WinDivertDevice.cpp>
2525
# Force light pcapng to be link fully static
2626
$<TARGET_OBJECTS:light_pcapng>
@@ -63,7 +63,7 @@ if(LINUX)
6363
list(APPEND public_headers header/LinuxNicInformationSocket.h)
6464
endif()
6565

66-
if(WIN32)
66+
if(WIN32 AND PCAPPP_USE_PCAP)
6767
list(APPEND public_headers header/PcapRemoteDevice.h header/PcapRemoteDeviceList.h header/WinPcapLiveDevice.h)
6868
endif()
6969

@@ -76,10 +76,11 @@ target_compile_features(Pcap++ PUBLIC cxx_std_14)
7676
if(APPLE)
7777
target_link_libraries(Pcap++ PRIVATE "-framework CoreFoundation" "-framework SystemConfiguration")
7878
elseif(WIN32)
79-
target_compile_definitions(Pcap++ PUBLIC -DHAVE_REMOTE)
80-
target_compile_definitions(Pcap++ PUBLIC -DWPCAP)
8179
target_link_libraries(Pcap++ PRIVATE ws2_32 iphlpapi)
82-
target_link_libraries(Pcap++ PUBLIC Packet::Packet)
80+
if(PCAPPP_USE_PCAP)
81+
target_compile_definitions(Pcap++ PUBLIC HAVE_REMOTE WPCAP)
82+
target_link_libraries(Pcap++ PUBLIC Packet::Packet)
83+
endif()
8384
endif()
8485

8586
target_include_directories(
@@ -103,7 +104,7 @@ target_link_libraries(
103104
$<$<BOOL:${PCAPPP_USE_DPDK}>:DPDK::DPDK>
104105
$<$<BOOL:${PCAPPP_USE_XDP}>:BPF::BPF>
105106
$<$<BOOL:${PCAPPP_USE_WINDIVERT}>:WinDivert::WinDivert>
106-
PCAP::PCAP
107+
$<$<BOOL:${PCAPPP_USE_PCAP}>:PCAP::PCAP>
107108
Threads::Threads
108109
)
109110

Pcap++/src/PcapFilter.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#if defined(_WIN32)
1010
# include <winsock2.h>
1111
#endif
12-
#include "pcap.h"
12+
#ifdef USE_PCAP
13+
# include "pcap.h"
14+
#endif
1315
#include "RawPacket.h"
1416
#include "TimespecTimeval.h"
1517

@@ -22,8 +24,12 @@ namespace pcpp
2224
{
2325
void BpfProgramDeleter::operator()(bpf_program* ptr) const noexcept
2426
{
27+
#ifdef USE_PCAP
2528
pcap_freecode(ptr);
2629
delete ptr;
30+
#else
31+
(void)ptr;
32+
#endif
2733
}
2834
} // namespace internal
2935

@@ -99,6 +105,7 @@ namespace pcpp
99105
if (m_FilterStr.empty())
100106
return true;
101107

108+
#ifdef USE_PCAP
102109
// Handle uncompiled program or link type mismatch
103110
if (m_CachedProgram == nullptr || linkType != static_cast<uint16_t>(m_CachedProgramLinkType))
104111
{
@@ -118,13 +125,18 @@ namespace pcpp
118125
pktHdr.len = packetDataLength;
119126
pktHdr.ts = internal::toTimeval(timestamp);
120127
return (pcap_offline_filter(m_CachedProgram.get(), &pktHdr, packetData) != 0);
128+
#else
129+
PCPP_LOG_ERROR("pcap is not available");
130+
return false;
131+
#endif
121132
}
122133

123134
BpfFilterWrapper::BpfProgramUPtr BpfFilterWrapper::compileFilter(std::string const& filter, LinkLayerType linkType)
124135
{
125136
if (filter.empty())
126137
return nullptr;
127138

139+
#ifdef USE_PCAP
128140
auto pcap = std::unique_ptr<pcap_t, internal::PcapCloseDeleter>(pcap_open_dead(linkType, DEFAULT_SNAPLEN));
129141
if (pcap == nullptr)
130142
{
@@ -140,6 +152,10 @@ namespace pcpp
140152

141153
// Reassigns ownership to a new unique_ptr with a custom deleter as it now requires specialized cleanup.
142154
return BpfProgramUPtr(newProg.release());
155+
#else
156+
PCPP_LOG_ERROR("pcap is not available");
157+
return BpfProgramUPtr(nullptr);
158+
#endif
143159
}
144160

145161
bool GeneralFilter::matchPacketWithFilter(RawPacket* rawPacket) const

Tests/ExamplesTest/pytest.ini

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ markers =
99
sslanalyzer: mark a test for SSLAnalyzer.
1010
pcapsearch: mark a test for PcapSearch.
1111
arping: mark a test for Arping.
12-
tlsfingerprinting: mark a test for TLSFingerprinting
13-
pcapsplitter: mark a test for PcapSplitter
12+
tlsfingerprinting: mark a test for TLSFingerprinting.
13+
pcapsplitter: mark a test for PcapSplitter.
14+
x509toolkit: mark a test for X509Toolkit.
1415
no_network: mark a test that does not need network connection.
16+
no_pcap: mark a test that does not require libpcap / WinPcap/ Npcap.

Tests/ExamplesTest/tests/test_pcapprinter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
class TestPcapPrinter(ExampleTest):
77
pytestmark = [pytest.mark.pcapprinter, pytest.mark.no_network]
88

9+
@pytest.mark.no_pcap
910
def test_sanity(self, tmpdir):
1011
args = {
1112
"": path.join("pcap_examples", "many-protocols.pcap"),
@@ -17,11 +18,13 @@ def test_sanity(self, tmpdir):
1718
expected_content="Finished. Printed 4709 packets",
1819
)
1920

21+
@pytest.mark.no_pcap
2022
def test_input_file_missing(self):
2123
args = {}
2224
completed_process = self.run_example(args=args, expected_return_code=1)
2325
assert "ERROR: Input file name was not given" in completed_process.stdout
2426

27+
@pytest.mark.no_pcap
2528
def test_print_count_packets(self, tmpdir):
2629
args = {
2730
"": path.join("pcap_examples", "many-protocols.pcap"),
@@ -46,6 +49,7 @@ def test_filter(self, tmpdir):
4649
expected_content="Finished. Printed 4666 packets",
4750
)
4851

52+
@pytest.mark.no_pcap
4953
def test_snoop(self, tmpdir):
5054
args = {
5155
"": path.join("pcap_examples", "solaris.snoop"),

Tests/ExamplesTest/tests/test_pcapsearch.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ def test_different_file_extensions(self):
5252
assert ".pcapng'" in completed_process.stdout
5353
assert ".pcap'" not in completed_process.stdout
5454

55+
@pytest.mark.no_pcap
5556
def test_no_args(self):
5657
args = {}
5758
completed_process = self.run_example(args=args, expected_return_code=1)
5859
assert "ERROR: Input directory was not given" in completed_process.stdout
5960

61+
@pytest.mark.no_pcap
6062
def test_invalid_dir(self):
6163
args = {"-d": "dir_that_doesnt_exist", "-s": "udp"}
6264
completed_process = self.run_example(args=args, expected_return_code=1)

0 commit comments

Comments
 (0)