Self-hosted service
mozillazg/ptcpdump avatar
mozillazg/ptcpdump

ptcpdump annotates tcpdump output with the process that sent it

Process-aware, eBPF-based tcpdump

1,291 stars68 forksCMIT

At a glance

What is it?
ptcpdump is an MIT licensed eBPF-based packet analyzer that keeps tcpdump's filter syntax and output while adding process, container and pod metadata to every packet, with PcapNG embedding and cross-namespace capture. It suits Linux troubleshooting where knowing which process owned a connection is the missing information, and it needs a BPF and BTF capable kernel.
Who is it for?
Adopt ptcpdump if you troubleshoot Linux networking and need to attribute traffic to a process, container or pod rather than only to an address and port. Do not adopt it as a general packet analyser for machines whose kernel lacks BPF and BTF support, since the README requires a kernel of 5.2 or newer with a specific set of configuration options.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 11 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 October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The same packet line, with three extra fields

ptcpdump is a tcpdump-compatible packet analyzer powered by eBPF, automatically annotating packets with process, container and pod metadata when detectable. The project description says the same thing more tersely: a process-aware, eBPF-based tcpdump.

What that looks like in practice is the clearest way to understand the tool. The default output line carries the usual tcpdump fields, the timestamp, the interface, the direction, the process and the addresses, and then three bracketed additions:

code
09:32:09.718892 vethee2a302f wget.3553008 In IP 10.244.0.2.33426 > 139.178.84.217.80: Flags [S], seq 4113492822, win 64240, length 0, ParentProc [python3.834381], Container [test], Pod [test.default]

ParentProc, Container and Pod. The parent process is the useful one, because a process name and PID tell you which program on the host is responsible for a connection, and in a container the pod name tells you which workload. The remaining fields are the standard tcpdump ones, so anything you already know how to read still works.

The README also shows a quieter mode with -q, which drops the tcp-level detail and keeps the process annotations, and a verbose mode with -v that expands the packet header fully, including the IP options, TTL, identification and flags.

The inspiration is credited to skbdump by jschwinger233, and the relationship to tcpdump is the point rather than an accident. The features list names tcpdump-compatible flags including -i, -w, -c, -s, -n, -C, -W and -A, support for the pcap-filter(7) syntax like tcpdump, and a tcpdump-like output with the process, container and pod context added. A tool that is a drop-in for tcpdump with extra columns is much easier to adopt than one with a new filter language, because the muscle memory transfers.

The filters that matter are the ones tcpdump does not have

The capture options follow tcpdump closely, and the filters are where ptcpdump adds something new.

You can filter by process with --pid, by process name with --pname, by container with --container-id, and by pod with --pod-name. The example commands show the combinations:

code
sudo ptcpdump -i any --pid 1234 --pid 233 -f
sudo ptcpdump -i any --pname curl
sudo ptcpdump -i any --uid 1000

Note that --pid can be given more than once, so you can watch several processes at once, and --pname filters by the executable name, which is what you actually want when you know that something is running curl but not its PID. The --uid flag filters by user.

For containers and pods the filters are by identifier or by name:

code
sudo ptcpdump -i any --container-id 36f0310403b1
sudo ptcpdump -i any --container-name test
sudo ptcpdump -i any --pod-name test.default

The pod-name form takes a namespaced value, test.default, which is the pod name and the namespace.

There is also a mode that ties the capture to a program you launch, which is a nicer way to work than guessing the PID:

code
sudo ptcpdump -i any -- curl ubuntu.com

The classic tcpdump filters work unchanged, including the byte-offset syntax and the flag-matching expression:

code
sudo ptcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'

And multiple interfaces can be captured at once, with -i eth0 -i lo, which is not something plain tcpdump supports in the same way and is what you need to see both ends of a local connection.

PcapNG with embedded metadata, and a pipe straight into tshark

The output format work is what makes ptcpdump more useful than a screen scraper, and there is a specific reason for it.

The features list names PcapNG with embedded metadata, described as Wireshark-ready. In other words, the process, container and pod annotations are written into the capture file itself, not just printed to the terminal. A capture file that carries the attribution stays useful after the fact, which matters for forensics and for incidents you analyse later rather than watch live.

The commands show the range of options:

code
sudo ptcpdump -i any -w demo.pcapng
sudo ptcpdump -i any -w - port 80 | tcpdump -n -r -
sudo ptcpdump -i any -w - port 80 | tshark -r -

The middle and last lines are the interoperability story. ptcpdump can write to standard output, so you can pipe its capture straight into tcpdump or into tshark, and the metadata travels with it. That means the tool does not force a choice between the process-aware view and the analysis tooling you already have.

The -C and -W flags carry over from tcpdump for file rotation, and the example combines them with the write flag:

code
sudo ptcpdump -i any -s 0 -n -v -C 100MB -W 3 -w test.pcapng 'tcp and port 80 and host 10.10.1.1'

A 100MB cap, three files, full packet data, numeric output, verbose headers, filtered to one host and port. That is a normal tcpdump invocation with the addition of the annotation, and it is the clearest demonstration that the tool is designed to be dropped into an existing workflow rather than to replace one.

The repository contains a docs/wireshark.png image, which suggests there is a documented Wireshark workflow, and the cross-namespace capture support extends the same idea to containers, where the interesting traffic is often in a namespace the host tools cannot see.

Cross-namespace capture reaches into containers the host tools cannot see

One feature stands out as the reason an eBPF implementation is necessary rather than merely convenient.

Cross-namespace capture, with the --netns flag, is shown as:

code
sudo ptcpdump -i lo --netns /run/netns/foo --netns /run/netns/bar
sudo ptcpdump -i any --netns /run/netns/foobar
sudo ptcpdump -i any --netns /proc/26/ns/net

The third form is the interesting one, since it takes a path under /proc rather than a named namespace, which means you can point it at the network namespace of a running process by its PID. The first form takes several namespaces at once, so you can capture the same interface name in two different namespaces and see both.

In a containerised environment this is the difference between seeing traffic and not. A pod's network is its own namespace, so a host-level tcpdump sees the CNI veth pair traffic rather than the pod's actual connections, and the answer to which process inside the pod is misbehaving is not available. The container and pod annotations in the output make the capture self-describing, and the --netns support makes the capture possible in the first place.

The last piece of the environment story is container runtime integration with Docker and containerd, listed in the features. The dependency list confirms how that integration is done. The Go module pulls in the containerd client and API, the moby client and API, and the Kubernetes CRI client and API packages, along with vishvananda/netns for network namespace handling. The CRI client and API are what allow a pod to be resolved to a pod name and namespace, and the containerd and moby clients cover the two runtimes in widest use.

The dependency list also includes mandiant/GoReSym and shirou/gopsutil, which cover the process-level metadata side. Together with the BPF backend, that is how the ParentProc field gets populated at all.

The kernel configuration table is the real installation guide

The requirements section is unusually explicit, and it is worth reading line by line before you try to run this on a machine.

The baseline is Linux kernel 5.2 or newer, compiled with BPF and BTF support. The README also says ptcpdump optionally requires debugfs, which has to be mounted in /sys/kernel/debug, and gives the command if the folder is empty: mount -t debugfs none /sys/kernel/debug.

Then there is the kernel configuration table, which lists an option, a backend and whether it is required, and it is long. Required for both backends are CONFIG_BPF, CONFIG_BPF_SYSCALL, CONFIG_DEBUG_INFO, CONFIG_DEBUG_INFO_BTF, CONFIG_KPROBES, CONFIG_KPROBE_EVENTS, CONFIG_TRACEPOINTS, CONFIG_PERF_EVENTS and CONFIG_NET. Required for the tc backend specifically are CONFIG_NET_SCHED, CONFIG_NET_CLS_BPF, CONFIG_NET_ACT_BPF and CONFIG_NET_SCH_INGRESS. Required for the cgroup-skb backend are CONFIG_CGROUPS and CONFIG_CGROUP_BPF. And CONFIG_FILTER is required for the socket-filter backend, with CONFIG_BPF_TRAMPOLINE required for the tp-btf backend.

Several options are marked optional but recommended, including CONFIG_SECURITY, CONFIG_BPF_TRAMPOLINE, CONFIG_SOCK_CGROUP_DATA and CONFIG_BPF_JIT.

The backends are the interesting part. There is a tc backend, a cgroup-skb backend, a socket-filter backend and a tp-btf backend, and the table maps options to backends. That is the project telling you there is more than one way to hook the packet path into the kernel, and each has different kernel support requirements. A distribution kernel will typically have some of these and not others.

The README gives a way to check, and it is a good one: you can use zgrep $OPTION /proc/config.gz to validate whether an option is enabled. That is a quick check on the machine you are about to run on, rather than reading documentation and hoping.

The features list also names kernel-space BPF filtering with low overhead that reduces CPU usage. That is a real advantage of doing the filtering in the kernel rather than copying every packet into userspace, and it is the reason the project is built on eBPF rather than on libpcap alone.

Go with an embedded eBPF layer and a vendored libpcap

The build system is where the project stops being a Go program with a syscall and starts being something more involved.

The repository has a bpf/ directory for the eBPF programs, a lib/ directory, internal/, cmd/, vendor/, a Dockerfile, a.goreleaser.yml, a Makefile and a.clang-format. The primary language is C because of the eBPF programs, with the Go side handling the user interface, the output formatting and the metadata resolution.

The libpcap handling is the part that requires attention. The Makefile has a libpcap target that builds libpcap from source with a long list of features disabled: --disable-shared, --disable-usb, --disable-netmap, --disable-bluetooth, --disable-dbus, --without-libnl and --disable-rdma. The result is a static libpcap.a that CGO links against, which is what lets the release artefacts be statically linked and therefore portable across distributions.

The Go link flags match that intent, with -linkmode external, -extldflags -fpie and -buildmode=pie, and the version and git commit are stamped into the binary through -X flags on internal.Version and internal.GitCommit.

The dependencies tell the rest of the story. cilium/ebpf at 0.18.0 is the eBPF loader, gopacket/gopacket for packet decoding, jschwinger233/elibpcap, florianl/go-tc for the traffic control backend, x-way/pktdump for packet dumping, spf13/cobra for the CLI, phuslu/log for logging, containerd/typeurl for the containerd type handling, and mandiant/GoReSym for the process name extraction.

The Go module targets 1.26.6, and the Dockerfile shows the two-stage pattern: a builder stage based on a quay.io development image that runs make build, and a final stage on busybox that copies only the binary, which is a very small image. The build image is pinned by digest, and so is the busybox base, which is a sign of a supply-chain-conscious project.

Editorial conclusion

Adopt ptcpdump if you troubleshoot Linux networking and need to attribute traffic to a process, container or pod rather than only to an address and port. Do not adopt it as a general packet analyser for machines whose kernel lacks BPF and BTF support, since the README requires a kernel of 5.2 or newer with a specific set of configuration options. Before you install, check the required kernel options with zgrep against /proc/config.gz, and remember that debugfs must be mounted at /sys/kernel/debug for the process and container resolution to work.

Frequently asked questions

What does ptcpdump add to tcpdump?

It keeps the tcpdump filter syntax, flags and output format, and annotates each packet with ParentProc, Container and Pod metadata when it can detect them. The project description calls it a process-aware, eBPF-based tcpdump.

How do I capture only the traffic of one process?

Use the process filters: --pid with a PID, --pname with an executable name such as curl, or --uid with a user id. You can also launch a program under capture with ptcpdump -i any -- curl ubuntu.com, and container and pod filters are --container-id, --container-name and --pod-name.

What kernel requirements does ptcpdump have?

Linux kernel 5.2 or newer compiled with BPF and BTF support, and a long list of required kernel configuration options that differ per backend, including CONFIG_BPF, CONFIG_DEBUG_INFO_BTF, CONFIG_KPROBES and CONFIG_TRACEPOINTS. The README suggests checking with zgrep $OPTION /proc/config.gz, and debugfs must be mounted at /sys/kernel/debug.

Can I capture inside other network namespaces?

Yes, with the --netns flag, which accepts a named namespace path such as /run/netns/foo, several at once, or a process path such as /proc/26/ns/net. The Go dependencies include vishvananda/netns along with the containerd, moby and Kubernetes CRI clients for container and pod resolution.

Can ptcpdump output be read by other tools?

It writes PcapNG with embedded metadata, described as Wireshark-ready, and it can write to standard output so a capture can be piped into tcpdump or tshark, for example ptcpdump -i any -w - port 80 | tshark -r -. The release artefacts are statically linked, with libpcap built from source with most optional features disabled.

Official sources

  1. Issues
  2. License: MIT
  3. mozillazg/ptcpdump on GitHub
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mozillazg-ptcpdump.svg)](https://hysenlabs.com/projects/mozillazg-ptcpdump)

Was this useful?