Packet analysis for .NET 10: read network and automotive captures, dissect them into typed fields, filter them
with Wireshark-style expressions, and export them to formats for tools, spreadsheets, and SQL. Use it as a
library, or from the command line with ni.
dotnet tool install -g NetworkInspector.CLI
ni export capture.pcapng -o packets.duckdb # every packet, every field, queryable with SQL- Enterprise and automotive traffic in one stack. Ethernet, IP, TCP/UDP, DNS, DHCP, HTTP/1.x, HTTP/2, TLS, WebSocket and CAN, CAN FD, CAN XL, LIN, FlexRay, SOME/IP, and JSON-configured signal decoding.
- Capture formats both worlds use. PCAP, PCAPNG, Vector BLF, and Vector ASC in and out, with round trips that keep interface metadata and bus details (CAN DLC 9–15, LIN protected id).
- Huge captures without huge RAM. Open, browse, and analyse captures with hundreds of millions of frames: random access costs well under 2 bytes of RAM per frame (see Large Captures With Bounded Memory).
- Fast. Millions of frames per second from memory-mapped files and streams, allocation-aware parsing with packet recycling, skip-parse modes, and Roaring-bitmap presence indexes for filtering huge captures.
- Analytics built in. Export to JSON, CSV, Text, PBF, Parquet, or a single DuckDB file with a field catalog and native types; record selected fields into columnar RAM caches while parsing.
- Wireshark-style filters, compiled.
tcp.port in {80, 443} && !tls, checked against the stack at compile time with "did you mean" hints, plus subtree scopes and edge detection across packets. - Live sessions for applications. Multi-source, multi-listener sessions with per-listener filters, background value caches, and hot protocol-stack swaps.
- Verified against Wireshark. More than 5,000 tests; dissectors, readers, writers, and the frame builder are
cross-validated against
tshark4.6. - Extensible with plugins. Add protocols, capture sources, export formats, and settings as a plugin DLL that
niloads from a folder; the built-in protocols, sources, and formats use the same contract (plugin guide). - Modular. Take only what you need: values, parser, readers, writers, filters, sessions, plugins, or the CLI.
NetworkInspector reads, parses, and analyses capture files far larger than RAM. Memory-mapped file sources keep only a compact frame index, and a session keeps only a packed packet → frame map; no frame bytes and no parsed packets stay on the heap. A capture with a billion CAN frames needs about 0.7 GB for random access instead of 38 GB, and a session over it stays browsable.
The index remembers only where each frame starts, packed in groups of 64 with bit-sliced deltas. Everything else is
re-read from the file by the same parser that read it the first time, so FrameById returns exactly the frame the
sequential read returned — or no frame, never a different one. While a source reads a file, Windows denies other
writers and Linux/macOS detect changes.
Retained managed memory per frame after a full scan, memory-mapped (measured A/B with NetworkInspector.Profiling,
scenarios footprint-*):
| Capture | Before | Now |
|---|---|---|
| PCAPNG, 1 M × 64-byte frames | 24.1 B | 0.40 B |
| PCAPNG, 500 k IMIX frames | 24.2 B | 1.58 B |
| BLF, 1 M CAN frames | 38.2 B | 0.67 B |
| ASC, 1 M CAN lines (absolute / relative time) | 32.0 B | 0.40 B / 1.71 B |
| Session over one PCAPNG source, per packet | 32.3 B | 1.87 B |
Details: Sources README, source guide §11.6.
dotnet tool install -g NetworkInspector.CLI
ni convert capture.blf -o capture.pcapng # Vector BLF -> Wireshark
ni export capture.pcapng -o packets.json # parsed packets as JSON
ni export capture.pcapng -o dns.json --filter "udp.port == 53" # only DNS
ni export capture.pcapng -o out -f parquet # Parquet dataset for pandas, Polars, Spark
ni settings set plugins.paths /opt/ni-plugins --scope application # load your plugins
ni plugins list # what each plugin contributesMore: CLI README.
dotnet add package NetworkInspector.Sources
dotnet add package NetworkInspector.Protocols
dotnet add package NetworkInspector.FilterRead a capture, parse every frame, and print the DNS packets:
using NetworkInspector.Core;
using NetworkInspector.Core.Ids;
using NetworkInspector.Core.Settings;
using NetworkInspector.Filter;
using NetworkInspector.Filter.Errors;
using NetworkInspector.Protocols;
using NetworkInspector.Sources.Pcapng;
// One registry for the stack and the source.
FrameInterfaceRegistry registry = new();
StackBuilder builder = new(new SettingsManager(), registry);
builder.RegisterStandardProtocols();
using Stack stack = builder.Build();
if (!Filter.TryCompile("udp.port == 53", stack, out Filter? dns, out FilterError? error))
{
throw new InvalidOperationException(error.ToString());
}
using PcapngSource source = PcapngSource.Open("capture.pcapng"); // PCAPNG and legacy PCAP
source.Start(registry.RegisterSource(source), registry);
ParseOptions options = new();
int nextId = 0; // first-parse ids are dense: 0, 1, 2, ...
while (source.NextFrame() is { } frame)
{
if (!Packet.TryParse(new PacketId(nextId), stack, frame, in options, out Packet? packet, out ParseFailure failure)
|| packet is null)
{
Console.Error.WriteLine($"frame {frame.Id.Value}: {failure}");
continue;
}
nextId++;
if (dns.TryIsMatch(packet, out bool matched, out _) && matched)
{
Console.WriteLine($"#{packet.Id.Value} {frame.Timestamp} {packet.Info}");
}
}Next steps: walk the field tree (Core), write the packets to a file (Exporters), or run the same pipeline on background threads with listeners (Sessions).
| Package | Version | Downloads | Purpose |
|---|---|---|---|
| NetworkInspector.Core | Parser engine, field tree, index, value cache | ||
| NetworkInspector.Protocols | Built-in dissectors | ||
| NetworkInspector.Sources | Read PCAP, PCAPNG, BLF, ASC | ||
| NetworkInspector.Exporters | Write capture and analysis formats | ||
| NetworkInspector.Exporters.DuckDb | Write a DuckDB database | ||
| NetworkInspector.Filter | Compiled display filters | ||
| NetworkInspector.Sessions | Multi-threaded capture sessions | ||
| NetworkInspector.FrameBuilder | Build wire frames from typed layers | ||
| NetworkInspector.Values | MAC, IP, EUI-64, UUID, timestamp values | ||
| NetworkInspector.Plugins | Plugin contract for sources, exporters, protocols | ||
| NetworkInspector.Plugins.Loader | Find, load, start, and stop plugins | ||
| NetworkInspector.BuiltInPlugins | Standard protocols, sources, and formats as plugins | ||
| NetworkInspector.CLI | ni command-line tool |
The source generator (NetworkInspector.Generators) ships inside
NetworkInspector.Core. Packages pull in their own dependencies: Sources, Exporters, and Filter bring Core;
Core brings Values; Plugins brings Core; Plugins.Loader brings Plugins; BuiltInPlugins brings Plugins, Protocols,
Sources, Exporters, and Exporters.DuckDb.
| Goal | Packages |
|---|---|
| Convert or export captures without code | NetworkInspector.CLI (tool) |
| Parse frames you already have | Core + Protocols |
| Read capture files | Sources (+ Protocols to parse) |
| Write captures or analysis files | Exporters (+ Exporters.DuckDb for DuckDB) |
| Filter packets | Filter (+ Protocols) |
| Build an analysis application with live views | Sessions + Protocols |
| Generate test traffic | FrameBuilder (+ Exporters to save it) |
| Typed addresses and timestamps only | Values |
| Write your own dissector | Core + Protocols (Protocol guide) |
| Write a plugin (protocol, source, exporter, setting) | Plugins (Plugin guide, Sample plugin) |
| Load plugins in your own application | Plugins.Loader + BuiltInPlugins (Loader README) |
| Layer | Protocols |
|---|---|
| Link | Ethernet II, VLAN (802.1Q, 802.1ad), LLC/SNAP, Linux SLL, Linux SLL2 |
| Network | IPv4, IPv6 (with fragment reassembly), ARP (including ATMARP), ICMPv4, ICMPv6 (Neighbor Discovery, Multicast Listener Discovery) |
| Transport | TCP (stream reassembly and analysis), UDP |
| Application | DNS, DHCPv4, DHCPv6, HTTP/1.x, HTTP/2, TLS, DTLS, WebSocket, JSON, Text |
| Automotive | CAN (classic, FD, XL), LIN, FlexRay, SOME/IP (service discovery, segmented messages, typed payloads from a service description, end-to-end protection, method timing), PDU Transport, signal messages |
Details and configuration: Protocols README.
| Format | Read | Write | Kind |
|---|---|---|---|
| PCAPNG | ✅ | ✅ | frames |
| PCAP (legacy) | ✅ | — | frames |
| Vector BLF | ✅ | ✅ | frames |
| Vector ASC | ✅ | ✅ | frames |
| JSON, CSV, Text | — | ✅ | packets |
| PBF (NetworkInspector binary) | — | ✅ | packets |
| Apache Parquet | — | ✅ | packets |
| DuckDB | — | ✅ | packets |
Measured with the NetworkInspector.Profiling scenarios on 50 000 PCAPNG frames (64 KiB frame slabs):
| Read path | Frames per second |
|---|---|
| Stream source, frames dropped | 7.55 M |
| Memory-mapped file, frames dropped | 3.72 M |
| Stream source, frames held | 3.45 M |
| Memory-mapped file, frames held | 2.40 M |
The parser recycles packets, decodes lazily, and can skip the field tree entirely when only values, indexes, or
filter verdicts are needed. Run NetworkInspector.Profiling to measure parse, filter, export, and session
throughput on your hardware. To compare two builds, run the newer build with
--ab <baselineExe> --repeat 5 --set <scenario,…> --out <csv> (interleaved child processes, one CSV row per run) and
turn the CSV into a Markdown table with a gate verdict through --report <csv> --out <md>. Add
--affinity 0x<mask> to pin every child to a processor set (for example the performance cores of a hybrid CPU), and
--warmup <s> / --duration <s> to shorten the phases of a series. --write-filter-verdicts <dir> --out <file> and
--write-session-digest <dir> --out <file> write byte-comparable correctness oracles for filter and session changes
over the captures in <dir> (for example the ones --write-golden-inputs <dir> writes).
- More than 5,000 tests (TUnit on Microsoft.Testing.Platform), including round trips through every capture format.
- Dissectors, capture readers and writers, and frame-builder output are cross-validated against
tshark4.6. - Warnings are errors; .NET analyzers and a style checker run on every build.
- Untrusted-input hardening: bounded lengths, counts, and nesting; decompression-bomb limits; process-seeded connection hashing; linear-time regular expressions where possible.
- Build a protocol stack once and reuse it; construction is single-threaded. First parses run in packet-id order; re-parses may run concurrently.
- Exporters and filters are single-threaded per instance.
Sessionis thread-safe. Package READMEs state the rules for each type. - Dispose stacks, sources, exporters, and sessions to release memory maps and files promptly. An open file source keeps its capture locked against writers (Windows) or checks it for changes (Linux/macOS).
- No live capture from network interfaces; inputs are capture files, streams, and in-memory frames.
- NativeAOT and trimming are not supported.
- Spoofing: Treat captures as untrusted input and check their provenance where it matters.
- Tampering: Expect malformed records; use tolerant processing and inspect the reported errors.
- Repudiation: Keep source metadata and command settings when results must be reproducible.
- Information disclosure: Captures and exports can contain credentials and personal data; control access.
- Denial of service: Use split, count, size, and cancellation limits on large or unknown inputs.
- Elevation of privilege: Run processing with least-privilege file-system and process rights.
Requires the .NET SDK 10.0.100 or newer (see global.json). Windows, Linux, and macOS on x64 and
ARM64.
dotnet build NetworkInspector.slnx -c Release
dotnet test NetworkInspector.slnx -c ReleaseThe cross-validation tests need tshark 4.6.x (part of Wireshark) on PATH:
| Platform | Install |
|---|---|
| Windows | Wireshark installer (includes tshark) or choco install wireshark |
| Ubuntu / Debian | sudo add-apt-repository ppa:wireshark-dev/wireshark && sudo apt-get install tshark |
| macOS | brew install wireshark |
Without Wireshark, set NETWORKINSPECTOR_ALLOW_MISSING_TSHARK=1 to skip the tests that opt into
TsharkAvailability.ShouldSkip(); all other tests still run. Do not set it in CI or release runs, so
cross-validation evidence is never discarded silently.
NetworkInspector.Core bundles NetworkInspector.Generators in its analyzer assets, and the library packages
have no third-party runtime dependency besides the ones listed in their package READMEs.