Packet.Net: dissecting and building network packets in C#
Official repository - High performance .NET package for dissecting and constructing network packets such as Ethernet, IP, TCP, UDP, etc.
At a glance
- What is it?
- Packet.Net is a .NET library for parsing and constructing network packets, with lazy field access, in-place views over the captured bytes and support for Ethernet, IPv4 and IPv6, TCP, UDP, ICMP, IGMP, L2TP, PPPoE, OSPF, ARP, LLDP, 802.1Q, 802.1ad and 802.11. It is MPL-2.0 licensed and ships as PacketDotNet on NuGet.
- Who is it for?
- Packet.Net suits a C# developer who needs to read or synthesise raw frames, whether for a network tool, a protocol test harness, a traffic analyser or a fuzzing setup, and the lazy in-place design is the reason it beats a byte-by-byte parser on hot paths. It is the wrong tool if you want a high-level protocol library, because there is no TCP client, no listener and no DNS layer here, only frames.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A frame library, not a networking library
Packet.Net is described as a high performance .NET package for dissecting and constructing network packets such as Ethernet, IP, TCP and UDP. It was originally created by Chris Morgan, and the description of the library makes its scope clear.
There is no socket, no connection, no listener and no application protocol in here. What you get is the layer below that: a way to take a buffer of bytes and ask what they mean, and a way to build a buffer of bytes from a description of what you want to send. That is a narrower job than most people expect from a .NET networking package, and the projects that need it tend to be unusual ones: a protocol analyser, a traffic shaper, a fuzzing harness, a test tool for a device that speaks a raw protocol, or a security tool that needs to inspect or forge frames.
The licence is MPL-2.0, which is a file-level copyleft licence. In practice that means modifying the library's own source and shipping a modified version carries an obligation to publish those changes, while using it as an unmodified dependency in your application does not require you to open your own code. That is a different arrangement from the GPL, and it is the reason MPL-2.0 is common for libraries in this space.
Lazy parsing: the object graph is built, the work is deferred
The performance section explains the design in a way that is worth repeating because it is the whole idea. The library aims to perform the most minimal amount of data processing in order to fully determine the datagram nesting.
Concretely, a TCP packet parses into a series of linked objects like Ethernet, then IPv4, then TCP, but no further data processing is performed until particular fields are accessed. The objects point to packet memory in place, avoiding allocation and copying of the packet contents unless necessary, such as when altering data payloads or resizing variable length fields.
Two consequences follow. The first is that parsing a packet and reading all of its fields are different costs, and a tool that only needs, say, the source and destination ports of a TCP header never pays for decoding options or checksums it will not use. The second is that the objects are views, not copies. A field you read is decoded on demand from the original buffer, and the buffer you handed in stays authoritative.
That in-place property is also what makes the library usable for a capture loop. You can point it at a buffer, walk the fields, and keep the buffer, rather than copying every frame out into a tree of objects. The cost of that choice is that a view is only valid while the underlying buffer is alive and unchanged, which is a real constraint for anyone holding a parsed packet beyond the lifetime of its buffer.
The protocol list and what it does not include
The supported formats list is long enough to cover most of what a traffic tool needs: Ethernet, IPv4 and IPv6, TCP, UDP, ICMP v4 and v6, IGMP v2 and v3, L2TP, PPPoE, OSPF, Wake-on-lan, IEEE 802.1Q, IEEE 802.1ad, IEEE 802.11, DRDA, ARP, LLDP, LSA, Linux SSL, PPP, and probably more, as the README puts it, pointing at the source code for the latest list.
Two entries are worth pausing on because they indicate how much beyond the obvious is covered. LSA is the link state advertisement used by OSPF, so the library can reach into the interior of a routing protocol rather than only its outer headers. Linux SSL is a specific encapsulation format, which tells you the library has been used on systems where TLS runs over a custom transport rather than over TCP.
What is missing is as informative as what is there. There is no application layer at all: no DNS, no HTTP, no TLS record parsing, no DHCP, no QUIC. There is also no storage layer for the higher protocols in the sense of a session or connection object, because the library models datagrams, not conversations. If your requirement is to read an HTTP header out of a stream, this library stops well before the layer you need and you would be combining it with something else.
Dissecting and constructing, from the Examples directory
The README points at a capture example under Examples/CapturingAndParsingPackets, and the repository listing shows two example directories, CapturingAndParsingPackets and ConstructingPackets. That pairing is the library's two halves made concrete: one shows how to get bytes and read them, the other shows how to assemble bytes and send them.
The capturing example is where the laziness becomes visible in practice. Rather than the library owning the capture, the example sits above whatever capture mechanism you use and hands the resulting buffer to Packet.Net for interpretation. That separation is what keeps the library free of platform-specific capture code and lets it work the same on Windows, Linux and macOS.
The constructing side is where the in-place design has a visible counterpart, because the library can both build a packet from scratch and mutate an existing one. The README's performance section mentions altering data payloads and resizing variable length fields as the cases where copying is unavoidable, which implies that writing into an existing buffer is a supported path rather than something you have to work around by rebuilding the frame.
Debug and Release builds differ in their dependencies
One build detail is unusual enough to be worth its own note, because it affects what ends up in your dependency graph. The Debug build depends on log4net and has log4net calls in some of its classes and code paths. The Release build does not depend on log4net and, taking advantage of conditional method attributes, does not include any calls to log4net methods, which ensures there is no performance impact on release builds.
This is the conditional compilation pattern where a method call is compiled out entirely rather than being present and skipped at runtime. The result is that a consuming project that references the release build does not inherit a logging framework dependency at all, which for a library that is supposed to run on hot paths is a meaningful detail rather than a footnote.
The practical consequence for a user is that debugging behaviour differs from production behaviour, and that log4net calls only exist in a build you would not normally ship. The repository listing also shows a Directory.Build.props at the root, which is where shared MSBuild properties for the solution live, and a PacketNet.sln.DotSettings file, which is the ReSharper settings file, so the solution carries its own shared configuration for both the build and the IDE.
The test suite doubles as the performance baseline
Packet.Net has a comprehensive suite of tests for each of the supported packet types, in the Test subdirectory. The performance benchmarks live in that same directory, and the README says they contain either performance or benchmark in their names.
What makes this worth reading is the method the README describes for anyone with a performance concern. You write a concise test that reproduces your usage case in a controlled manner, then run and re-run that test case in various profiling modes to look at potential ways of optimising the code. The tests also provide a baseline against which to compare proposed improvements, so that changes are not inadvertently reducing performance rather than increasing it.
That is a disciplined way to handle a performance claim, and it is better than the usual arrangement where benchmarks live in a separate project that stops being run. Here the benchmark and the correctness suite are the same codebase, so a change that breaks a parser and a change that slows a parser are both caught by the same command.
The repository also carries an ArchitectureNotes entry at the top level, which suggests the design decisions behind the lazy parsing model are documented somewhere in the tree for contributors, and a Scripts directory for build automation.
Where Packet.Net is the wrong choice
Three limits are structural. The first is the layer it operates at. If you want to make an HTTP request, read a REST API or run a TCP server, this library cannot help, because it deals in frames and offers no session handling. The alternative in that direction is a higher-level library built on sockets, and the difference in approach is not that one is better but that the higher-level one hides the frame boundary entirely, which is precisely what a traffic tool needs to keep visible.
The second is the buffer lifetime constraint that comes with in-place views. Because the parsed objects point into the original memory, a parsed packet is only meaningful while that buffer lives and stays unmodified. Code that parses frames from a receive loop and then hands the objects to another thread has to own the buffer's lifetime explicitly, which is more care than a copying parser would demand.
The third is the maintenance pace. The releases are v1.4.8 from 2025-04-22, v1.4.7 from 2022-10-19 and v1.4.6 from 2022-05-21, so the gap to the most recent release was two and a half years, and the last push to the repository was on 2026-04-07. The repository is not archived, but a library with such sparse releases is one to fork rather than to wait on. The NuGet package is PacketDotNet, and the README's own links still point at the chmorgan/packetnet repository paths, which is a small sign that the project has moved homes without every reference being updated.
Editorial conclusion
Packet.Net suits a C# developer who needs to read or synthesise raw frames, whether for a network tool, a protocol test harness, a traffic analyser or a fuzzing setup, and the lazy in-place design is the reason it beats a byte-by-byte parser on hot paths. It is the wrong tool if you want a high-level protocol library, because there is no TCP client, no listener and no DNS layer here, only frames. Verify first which protocol formats you actually need, since the supported list is finite, and note that the newest release is v1.4.8 from 2025-04-22 with the last push to the repository on 2026-04-07.
Frequently asked questions
What is Packet.Net and what does it do?
It is a .NET package for dissecting and constructing network packets such as Ethernet, IP, TCP and UDP, with support for around twenty protocol formats. It parses lazily, so a packet becomes a chain of linked objects and field processing only happens when a field is accessed.
How does Packet.Net avoid copying packet data?
The parsed objects point to packet memory in place, so allocation and copying of the packet contents are avoided unless necessary, such as when altering data payloads or resizing variable length fields. The library aims to do the minimal amount of data processing needed to determine the datagram nesting.
Which protocols does Packet.Net support?
Ethernet, IPv4 and IPv6, TCP, UDP, ICMP v4 and v6, IGMP v2 and v3, L2TP, PPPoE, OSPF, Wake-on-lan, IEEE 802.1Q, IEEE 802.1ad, IEEE 802.11, DRDA, ARP, LLDP, LSA, Linux SSL and PPP, with the README pointing at the source code for the latest list.
Where do I get Packet.Net and how do I install it?
It is published on NuGet as PacketDotNet, and the README links the capture example under the Examples directory of the repository, with basic examples in the Examples folder. The README does not give a separate install command, so the NuGet package and the example projects are the documented entry points.
Why does the Debug build of Packet.Net depend on log4net but the Release build does not?
The Debug build has log4net calls in some classes and code paths, while the Release build uses conditional method attributes so those calls are not included at all, which ensures there is no performance impact on release builds.
How are performance changes tested in Packet.Net?
The Test directory contains benchmarks whose names contain performance or benchmark, and the README describes writing a concise test that reproduces your usage case in a controlled manner, then running it in various profiling modes. Those tests also provide a baseline to check that a proposed improvement does not reduce performance.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dotpcap-packetnet)Was this useful?