A real-time and offline network traffic analyzer built with Modern C++20, Npcap/libpcap, and Dear ImGui.
Every push is built on Windows, Linux, and macOS, run under AddressSanitizer and UndefinedBehaviorSanitizer, checked with clang-tidy, and fuzzed with libFuzzer.
Download the ZIP for your platform from GitHub Releases. Windows releases require the Npcap driver for live capture and PCAP support. Linux and macOS releases require their system libpcap runtime.
- Live and offline capture: inspect an active adapter or drag a
.pcap/.pcapnginto the app (classic libpcap and modern PCAPNG both supported). - Link-layer aware: decodes Ethernet, VLAN/QinQ, Linux cooked captures (
SLL/SLL2, as produced by theanydevice), BSD/OpenBSD loopback, and raw-IP captures. Exported PCAPs preserve the original link type. - Protocol insight: IPv4/IPv6, TCP, UDP, ARP, ICMP/ICMPv6, SCTP, and named non-IP frames (LLDP, EAPOL, PPPoE, MPLS).
- Tunnel descent: transparently decodes GRE, IP-in-IP, 6in4, VXLAN, GENEVE, MPLS, and PPPoE, so flows are keyed on the real inner endpoints rather than the tunnel. Encrypted tunnels (ESP/IPsec, WireGuard, OpenVPN) are labelled as such instead of being silently misreported.
- Deep application identification:
- TLS SNI on any port (content-sniffed, not port-gated), with reassembly of ClientHellos split across TCP segments — required now that post-quantum key shares push the message past one MSS.
- QUIC ClientHello SNI for v1 (RFC 9000) and v2 (RFC 9369) on any UDP port, decrypting Initial packets and reassembling CRYPTO frames across multiple Initials.
- Cleartext HTTP: request line and
Host:header, which names hosts even when no DNS was observed. - DNS/mDNS: A/AAAA/CNAME, plus PTR (names LAN devices that never appear in a forward lookup), SRV, TXT, and HTTPS/SVCB records.
- Encrypted-DNS visibility: detects DoH/DoT/DoQ and Encrypted Client Hello, and tells you when name resolution has moved off the wire rather than silently showing bare IPs.
- Packet detail pane: click any packet to see a structured Frame → Tunnel → Network → Transport → Application decode plus an
xxd-style hex dump of the raw bytes. - Flows view: sortable per-connection byte totals, one-second rate, initial TCP RTT (from the SYN / SYN-ACK delta, color-coded by latency), duration, per-flow retransmission and out-of-order rates, service, hostname, owning process, Country, and ASN/organization, with a detail pane and live rate sparkline. Flow keys are canonical, so both directions of a peer-to-peer conversation collapse into a single row.
- Statistics view: protocol hierarchy, top talkers by bytes, and name-resolution health.
- Process resolution: the App column in both the packet list and the flows table shows the owning process on Windows (iphlpapi), Linux (
/proc/net/*+/proc/<pid>/fd/), and macOS (proc_pidfdinfo). Flows capture the process while the socket is live, so the name persists after a short-lived connection closes. Running elevated widens coverage to other users' processes. - Filtering and export: live BPF capture filters such as
tcp port 443, a separate display filter over captured packets, PCAP export, and flow export to CSV or JSON. - Light and dark themes, adjustable UI scale, and keyboard shortcuts — all persisted between runs.
- Cross-platform backends: Npcap on Windows and libpcap on Linux/macOS.
| Live packet analysis | Connection flows | Offline PCAP mode |
|---|---|---|
| Protocol, hostname, and per-packet hex | Rate, service, GeoIP, and ASN | No administrator rights required |
- Language: C++20
- Packet Capture: Npcap SDK (Windows) / libpcap (Linux/macOS)
- UI Framework: Dear ImGui + GLFW with docking
- Plotting: ImPlot
- Crypto (QUIC Initial decryption): Mbed TLS — HKDF-SHA256, AES-128-ECB, AES-128-GCM
- GeoIP/ASN: libmaxminddb
- File dialogs: Native File Dialog Extended
- Build System: CMake (all third-party deps pinned via
FetchContent) - Tests / fuzzing / analysis: GoogleTest + libFuzzer harness on the protocol parsers + clang-tidy
NetProbe uses Npcap on Windows and libpcap on Linux/macOS.
- Windows 10/11: Visual Studio 2022 with C++ Desktop Development, the Npcap driver, and the Npcap SDK extracted to
C:\Npcap-SDK(IncludeandLibdirectly inside). - Ubuntu/Debian:
sudo apt install build-essential cmake libpcap-dev libgl1-mesa-dev libgtk-3-dev pkg-config - macOS: Xcode Command Line Tools and CMake. libpcap and OpenGL are provided by macOS; Native File Dialog uses Cocoa.
# 1. Clone the repository
git clone https://github.com/YehiaGewily/netprobe-cpp.git
cd netprobe-cpp
# 2. Build via Script (Recommended)
.\build_project.batOn Linux or macOS:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
ctest --test-dir build --output-on-failureTo create the same ZIP layout used by Releases:
cpack --config build/CPackConfig.cmake -C Release -G ZIP -B package| Flag | Default | What it does |
|---|---|---|
-DBUILD_TESTING=ON |
ON when top-level |
Build the GoogleTest suite (NetProbeTests). |
-DENABLE_SANITIZERS=ON |
OFF |
Build with -fsanitize=address,undefined (Clang/GCC only). |
-DBUILD_FUZZERS=ON |
OFF |
Build the libFuzzer harness for the protocol parsers. Requires clang / clang-cl. |
-
Run the executable from the extracted release ZIP or build output directory.
-
No admin? Try the sample first. The build emits
data/sample.pcap(mixed ARP / DNS / TCP traffic: three resolvable hostnames, each with a full TLS handshake and a measurable RTT). Open it via File → Open PCAP… to exercise the full UI without elevation. -
Live capture usually requires elevated privileges. On Windows:
Start-Process ".\build\Release\NetProbe.exe" -Verb RunAs
-
Select a live adapter in the menu bar, or choose File → Open PCAP… / drop a PCAP file onto the window.
-
Use Flows for per-connection activity, or enter a BPF filter such as
tcp port 443in the menu bar. -
Click a packet row in Live Packets to populate the Packet Detail pane with the layered decode and hex dump.
For GeoIP and ASN columns, place GeoLite2-Country.mmdb and GeoLite2-ASN.mmdb in data/. See data/README.md.
netprobe-cli runs the same capture and analysis pipeline with no window, for servers, CI, and SSH sessions. It ships alongside the GUI in every release ZIP.
# List the adapters available for live capture
netprobe-cli --list-devices
# Replay a capture file and export its flows as JSON
netprobe-cli -r capture.pcap -o flows.json
# Capture live for 60 seconds, filtered, exporting CSV
netprobe-cli -i eth0 -f "tcp port 443" --duration 60 -o flows.csv
# Pipe JSON straight into jq
netprobe-cli -r capture.pcap -o - | jq '.flows[] | select(.bytes_down > 100000)'| Flag | Purpose |
|---|---|
-r, --read <file> |
Replay an offline capture |
-i, --device <name> |
Capture live from an adapter (needs privileges) |
-f, --filter <bpf> |
BPF capture filter, live capture only |
-o, --output <path> |
Destination file, or - for stdout |
--format <json|csv> |
Override the format inferred from the extension |
--duration <sec> |
Stop after this many seconds, live capture only |
--packet-count <n> |
Stop after this many packets, live capture only |
--no-process |
Skip the owning-process lookup |
--list-devices |
List the capture adapters and exit |
-h, --help |
Show the full usage text and exit |
-V, --version |
Show the version and exit |
Ctrl+C stops a live capture and still writes the output. Exit codes are 0 success, 1 usage error, 2 runtime failure, so scripts can tell a bad invocation from a failed capture. Diagnostics go to stderr, which keeps -o - output clean for piping.
NetProbe is built around a bounded producer-consumer pipeline. Capture runs on a backend-owned path, while the Dear ImGui frame loop drains packets, enriches them, and updates view models without blocking the capture thread.
Figure 1: Runtime Component Map
Figure 2: Packet Decode And Enrichment Pipeline
Figure 3: Live Capture Control Flow
Figure 4: Offline PCAP Path
flowchart LR
file[".pcap / .pcapng file"] --> open["CaptureEngine::openFile"]
open --> backend["pcap offline reader"]
backend --> dlt["Snapshot link type<br/>for correct export DLT"]
backend --> packets["Read packets until EOF"]
packets --> session["Retain recent session packets"]
packets --> queue["Push into PacketQueue"]
queue --> ui["GuiLayer frame loop"]
ui --> same["Same parser, DNS/SNI recovery,<br/>flow aggregation, and views as live mode"]
- Capture backend (
src/capture/):ICaptureBackendhides the Npcap and libpcap implementations.CaptureEngineowns the backend, live capture thread, BPF application, offline reads, and a capped session buffer for PCAP export. - Packet queue (
src/core/PacketQueue.hpp): thread-safe bounded queue (std::mutex+std::condition_variable). On overflow it drops the oldest packets so the UI stays close to live traffic; the dropped count is surfaced in the control bar. - Protocol stack (
src/core/):LinkTypemaps libpcapDLT_*values to supported encapsulations, so cooked, loopback, raw-IP, and Ethernet-family captures are decoded intentionally.ProtocolParseris the stateless fast path: link layer -> IPv4/IPv6 -> tunnel descent -> transport -> application hints and service classification.TlsReassemblerbuffers split TCP ClientHellos with size and expiry caps.QuicParserdecrypts QUIC v1/v2 Initial packets with mbedTLS and extracts TLS ClientHello data from CRYPTO frames.QuicTrackerstitches CRYPTO fragments across multiple Initial packets, keyed by connection id.DNSParserextracts DNS/mDNS/LLMNR answers, feedsHostnameCache, and flags advertised ECH configs.FlowAggregatorcollapses both directions of a conversation into one canonical key, tracks rates and byte totals, and measures initial TCP RTT from SYN/SYN-ACK timing.GeoIPResolverandProcessResolveradd country/ASN and process ownership where platform data is available.AnalysisSessiondrives the whole per-packet pipeline — decode, cross-packet SNI recovery, DNS harvesting, hostname attribution, process lookup, flow aggregation — and owns the stateful reassemblers, the name cache, and the flow table. The GUI and the headless CLI both run this same object, which is what keeps them from drifting apart.FlowExporterserializes a flow snapshot to CSV or JSON.
- GUI layer (
src/ui/):GuiLayerowns the Dear ImGui dockspace, capture controls, settings, queue drain, and bounded packet history, layered over anAnalysisSession.Dashboard,FlowsView,PacketView,PacketDetail, andStatsViewrender those derived models. - Headless CLI (
src/cli/): the sameCaptureEngineandAnalysisSessionwith no window, exporting flows for scripts and pipelines.
- 89 unit and integration tests (GoogleTest) covering protocol parsing, link-type handling, tunnel descent, TLS/QUIC ClientHello reassembly, HTTP header extraction, DNS record coverage, canonical flow keying, RTT measurement, queue concurrency, GeoIP lookup, QUIC Initial decryption end-to-end, and both classic-PCAP + PCAPNG fixtures — including an end-to-end test that drives a crafted capture file through the same pipeline the UI runs, and end-to-end tests that run the real
netprobe-clibinary over a capture file and validate the exported JSON and CSV. - Adversarial input suite: 40 named malformed-packet fixtures — headers claiming lengths the buffer does not contain, IPv4 IHL and total-length lies, IPv6 extension-header chains that overrun or self-reference, DNS compression-pointer loops and inflated record counts, truncated TLS records, QUIC connection-ID and token lengths beyond the packet — plus TCP option lengths of zero and options overrunning their header, and an exhaustive check that every prefix of a valid frame parses without crashing. Each fixture pins the degraded result it must produce, so a parser that starts inventing plausible answers fails the build rather than passing quietly. The same fixtures seed the fuzz corpus, taking it to 44 entries.
- libFuzzer harness on
ProtocolParser::parse,DNSParser::parseResponse, and the statefulTlsReassembler/QuicTracker, across every supported link type, with a deterministic seed corpus. CI fuzzes every push for 60 seconds on Ubuntu + Clang and uploads any crash inputs as build artifacts. - AddressSanitizer + UndefinedBehaviorSanitizer CI job runs the full test suite under ASan/UBSan on every push.
- clang-tidy static analysis in CI over every first-party source, with
WarningsAsErrorsenabled: the tree is clean, so a new finding fails the build rather than scrolling past in a log. Checks are curated for signal — those that fire constantly on idiomatic code are disabled in.clang-tidywith the reasoning recorded there, rather than silenced case by case. - Three-platform CI matrix (Windows / Ubuntu / macOS) builds the app, builds and runs the test suite, and packages release ZIPs via CPack on tag pushes. Windows test runs use a 7-Zip-extracted Npcap user-mode DLL so the kernel-driver install is unnecessary in CI.
Contributions are welcome. See CONTRIBUTING.md for build instructions on each platform, how to run the same checks CI runs (tests, sanitizers, clang-tidy, fuzzing), and what review looks for in a tool that parses hostile input. It also lists some good first issues.
This project is licensed under the MIT License — see LICENSE for the full text.