PF_RING: a kernel module and user-space framework for high-rate packet capture on Linux
High-speed packet processing framework
At a glance
- What is it?
- PF_RING is a Linux kernel module plus a user-space library that gives packet processing applications one API across NICs and drivers. It is aimed at anyone whose packet rate has outgrown stock capture, and the trade-offs sit in the kernel build and the licence split.
- Who is it for?
- Adopt PF_RING if you already run Linux packet processing and need one API across several NICs and drivers at rates stock capture cannot sustain, and you can build kernel modules against your running kernel. Do not adopt it if you cannot rebuild modules per kernel, or if a user-space-only design with a different licence split fits your product better.
- Can I use it commercially?
- Yes, with conditions. LGPL-2.1 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?
- Yes. The repository last received commits 9 days 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 September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What PF_RING solves, and who is actually shopping for it
Stock Linux capture paths were not built for line rate. PF_RING's README puts the range bluntly: what counts as "many" packets per second depends on the hardware, from 80k pkt/sec on a 1.2GHz ARM to more than 20M pkt/sec per core on a low-end 2.5GHz Xeon. That second number is the one that matters. Per core, not per box. So the project is for people whose bottleneck is the capture path itself rather than the analysis code sitting behind it.
The README also makes a claim that is easy to skim past: PF_RING "not only enables you to capture packets faster, it also captures packets more efficiently preserving CPU cycles." Read that as the actual pitch. Saving CPU cycles on capture leaves more of the same core for whatever you do with the packet afterwards, which is why the project shows up under intrusion detection and traffic analysis rather than only under packet archiving.
The audience is narrower than "everyone who handles packets." You need to be on Linux, because the core is a kernel module. You need to be willing to build it, because the top-level Makefile recurses into kernel, userland and drivers directories rather than shipping a single portable build. And you need a reason to care about the per-core figure. A tool that reads a few thousand packets a second on a developer laptop gains nothing here except build complexity.
The kernel module, the library, and where packets actually go
The architecture is two pieces. A Linux kernel module sits below the normal networking stack and handles packet acquisition. A user-space library above it exposes the API that applications call. The point of the split is that the application does not need to know which driver or which capture mechanism is underneath. That is what the README means by "a consistent API for packet processing applications."
The repository layout reflects the split. The top-level entries include kernel/, userland/, and drivers/ as separate trees, plus tools/, doc/ and package/. A single top-level Makefile drives all three: it descends into kernel and runs make, descends into userland and runs ./configure followed by make, then descends into drivers and runs make. Installation is a separate step that only touches user space, running make install inside userland.
That asymmetry is worth pausing on. The kernel module and drivers are built but not installed by the top-level install target. If you are expecting make install to place a loadable module for you, the Makefile does not do that. You handle module placement and loading yourself, which is also where per-kernel rebuilds become your problem rather than the project's.
The drivers/ tree is where the consistent-API idea gets real. Vendor and in-tree drivers are adapted so that the same user-space call works regardless of what the NIC is. The cost of that consistency is that the driver set has to track kernel changes, and that is the part of the project most exposed to churn.
Building PF_RING and capturing your first packets
The README points to the User's Guide at ntop.org/guides/pf_ring and the API documentation at ntop.org/guides/pf_ring_api for the detail, and the repository ships a README.FIRST file at the top level that is worth reading before anything else. What the repository itself shows is the build sequence. From the repository root, the top-level Makefile is the entry point:
makeThat one target runs three things in order: make inside kernel/, ./configure and make inside userland/, then make inside drivers/. Expect the kernel step to need headers for the kernel you are actually running, because it compiles a module. If that step fails, nothing downstream is worth debugging yet.
Installation covers user space only:
make installThis runs make install inside userland/. It does not install the kernel module or the drivers. Those you place and load yourself, and you will repeat that whenever your kernel changes.
There are convenience targets for the two most common consumers. To build the tcpdump that links against PF_RING rather than the system libpcap:
make tcpdumpThat descends into userland/tcpdump, runs ./configure, then make. For Snort, the target builds two DAQ modules, the standard one and a zero-copy variant:
make snortThis runs autoreconf -ivf, ./configure and make inside userland/snort/pfring-daq-module and again inside userland/snort/pfring-daq-module-zc. The zero-copy module is the one people search for as pf_ring zc, and it is a separate build artifact from the standard DAQ module, not a flag on it.
Where PF_RING stops being the right answer
The clearest limitation is the one the Makefile states without commenting on it. This is a kernel module. It has to be compiled against a specific kernel and loaded into it. On a distribution that ships kernels frequently, or on a host where you do not control kernel updates, that turns into recurring work: rebuild, replace, reload, verify. Nothing in the README or the Makefile offers a way around it, and the top-level install target deliberately does not try.
The second limitation is scope. PF_RING gives you a faster path to packets and a consistent API. It does not tell you what to do with them. If your analysis code is the bottleneck, or your storage is, a faster capture path just moves the queue. The README's own framing is about capture speed and CPU efficiency, not about downstream processing.
The third is that the licence is split by component, and the split is not cosmetic. The README states that the kernel module and drivers are under GNU GPLv2, while the user-space PF_RING library is LGPLv2.1. So the part you link your application against and the part you load into the kernel carry different obligations. If you are shipping a proprietary product, which side of that line your code sits on is a question for your own counsel, not something the README resolves for you.
Finally, the project assumes Linux. There is no non-Linux story in the README, and the kernel module makes that structural rather than a packaging gap.
PF_RING against DPDK and against plain AF_PACKET
The comparison people actually search for is pf_ring vs dpdk, and the difference is architectural rather than a matter of tuning. PF_RING is a kernel module plus a user-space library: packets still traverse a kernel component, and your application keeps using a socket-like API. DPDK is a user-space data plane that takes over the NIC through poll-mode drivers, bypassing the kernel path. That means DPDK asks you to restructure the application around its environment and give it dedicated cores, while PF_RING asks you to build and maintain a kernel module but lets existing capture code keep its shape.
Against AF_PACKET, the difference is smaller and more practical. AF_PACKET is already in the kernel, needs no build step and no module to maintain. PF_RING exists because that is not always fast enough, and it layers driver support and a consistent API on top. If AF_PACKET at your packet rate is fine, PF_RING adds a build and a rebuild cycle for headroom you are not using.
The repository makes the DPDK-adjacent position visible in a small way: the Snort target builds two DAQ modules, pfring-daq-module and pfring-daq-module-zc. The zero-copy variant is the one that trades the most for speed, and it is a distinct artifact you choose to build and deploy.
Maintenance, releases and what the licence split costs you
The last push to the repository was on 2026-09-21, and the most recent release is 9.4.0 from 2026-08-29. Before that, 9.2.0 landed on 2025-11-17 and 9.0.0 on 2025-04-28. The cadence is not frantic, and the version numbering jumps by minor increments rather than patch releases, so you should not expect a stream of small fixes between those points.
Your upgrade cost is mostly your own build pipeline, not the project's release notes. Because the kernel module must match the running kernel, every kernel update on a host running PF_RING is a potential rebuild. The top-level Makefile supports this directly: make builds kernel, userland and drivers together, and there is a clean target that descends into kernel, userland and drivers, plus a separate clean for userland/snort/pfring-daq-module. Those targets are the mechanical part. The operational part, deciding when to rebuild and reload on a live capture host, is yours.
On licensing, the README is explicit: GPLv2 for the kernel module and drivers, LGPLv2.1 for the user-space library. The practical consequence is that the two halves of your integration are governed differently. Whether that matters depends on how you distribute your application and whether it links the user-space library or only talks to the kernel side. That is a legal question about your specific product, and the repository does not answer it.
Editorial conclusion
Adopt PF_RING if you already run Linux packet processing and need one API across several NICs and drivers at rates stock capture cannot sustain, and you can build kernel modules against your running kernel. Do not adopt it if you cannot rebuild modules per kernel, or if a user-space-only design with a different licence split fits your product better. Before committing, check the README.FIRST file and the User's Guide for the exact kernel and driver build steps for your distribution, and confirm which of the kernel module or the user-space library your own code links against, because the two carry different licences.
Frequently asked questions
What is PF_RING?
It is a Linux kernel module and user-space framework that lets applications process packets at high rates through a consistent API, so the same call works across different drivers and NICs.
How does PF_RING compare with libpcap?
The README does not discuss libpcap directly. It describes PF_RING as a kernel module plus user-space library that captures packets faster and more efficiently than a stock path, and the repository builds a PF_RING-linked tcpdump through its own make tcpdump target, which implies the two are separate capture paths rather than the same one.
Should Suricata use PF_RING or AF_PACKET?
The README does not cover Suricata configuration. What the repository does show is that PF_RING ships DAQ modules for Snort, including a zero-copy variant, which is the same integration pattern Suricata would need. AF_PACKET requires no build or module maintenance, while PF_RING requires building and rebuilding a kernel module.
How does PF_RING zero copy compare with DPDK?
PF_RING keeps a kernel component in the path and gives applications a consistent socket-like API, while DPDK is a user-space data plane that bypasses the kernel. The repository builds a zero-copy DAQ module as a separate target, pfring-daq-module-zc, distinct from the standard one.
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/ntop-pf-ring)