OPNsenseXXXVI · Packet Capture and DiagnosticsPacket capture fundamentals
Packet capture fundamentals — what tcpdump actually shows and what it leaves out
What you'll learn
- Describe how packet capture works on FreeBSD and where it sits relative to PF
- Explain the difference between the live packet and the captured copy
- Identify what capture shows and what it deliberately hides
- Choose the right capture interface for a given troubleshooting question
Prerequisites
Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-14
A packet capture is the firewall operator’s strongest evidence and the most common source of misdiagnosis. A capture shows what crossed the wire at the moment the capture ran. It does not show why the packet arrived or did not arrive, what PF decided about it, or what the application did with it. The operator who reads a capture as a complete picture misreads the network; the operator who reads a capture as a partial witness, to be cross-checked against PF state and routing tables, reads it correctly.
This lesson covers how capture works on FreeBSD/OPNsense, the difference between the captured copy and the live packet, what the capture deliberately hides, and the discipline of choosing the right interface for the question.
How capture works
Packet capture on FreeBSD uses libpcap. libpcap attaches to the network interface in a special mode — typically link-layer — and receives a copy of every frame that arrives (and, optionally, every frame that leaves). The kernel uses a Berkeley Packet Filter (BPF) to decide which frames to copy across to userspace. tcpdump, the GUI live capture page, and Suricata are all consumers of libpcap on OPNsense.
The discipline is the order of operations:
- The NIC receives the frame and hands it to the kernel.
- The BPF filter evaluates the frame and, if it matches, copies it to a userspace buffer.
- The kernel continues to process the frame normally — through the IP stack, into PF, and out the egress interface.
- The userspace tool reads the buffer and prints or saves the frame.
Step 3 is the critical point: the capture is a witness, not the actor. The packet is forwarded (or dropped) by PF regardless of whether the capture saw it. A rule that drops a packet drops it whether tcpdump is running or not.
$ tcpdump -ni igb0 -c 1tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on igb0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:34:56.789012 aa:bb:cc:11:22:33 > 66:77:88:99:aa:bb, IPv4, length 84: 192.0.2.50.51820 > 8.8.8.8.53: UDP, length 42Illustrative output
The capture is a copy
The BPF tap runs in parallel with the kernel’s normal packet processing. The frame is delivered to the kernel’s IP stack exactly once and to the BPF queue exactly once. PF does not know the capture is happening. The application does not know. The peer does not know.
This has three consequences:
- Capture cost is real. Copying every frame to userspace costs CPU. On a busy link, tcpdump running at full packet rate can add 10-20% CPU. The operator should always capture with a filter (
-iplus a BPF expression) and stop the capture when done. - Capture shows the wire, not the decision. The frame the kernel forwarded is the frame the capture saw — but PF’s decision (pass/block) is not in the capture. The operator must cross-check against the firewall log.
- Capture timing is approximate. The timestamp in the capture is when libpcap read the frame from the kernel buffer, not when the frame hit the wire. The skew is microseconds, but it exists.
What capture shows and what it hides
A capture shows:
- The wire format. Every byte from the Ethernet header onwards, unless filtered out.
- The MAC addresses. Source and destination, including VLAN tags.
- The IP layer. Source, destination, protocol, length, TTL, flags, options.
- The transport layer. Source and destination ports, sequence/ack numbers (TCP), length (UDP), type/code (ICMP).
- The payload. Unless the kernel strips it (it does not, by default). Encrypted payloads appear as random bytes.
A capture hides:
- The firewall decision. Capture cannot tell you whether PF passed or dropped the packet. PF is downstream of the capture point.
- The routing decision. Capture cannot tell you which interface the kernel would have chosen as the egress. The frame the capture saw is the one that already arrived; if the kernel routed it, the egress frame will appear on a capture of the egress interface.
- The state table. Capture cannot tell you which PF state this packet belongs to. The state is internal to PF.
- The application response. Capture shows what came back from the server, not what the application did with it.
The operator must gather firewall logs, PF state, and routing evidence alongside the capture to build a complete picture.
Choosing the capture interface
The single most important choice in any capture session is which interface to capture on. The answer depends on the question:
- “Did the packet reach the firewall at all?” Capture on the ingress interface. If you see the packet, the firewall saw it.
- “Did PF let the packet through?” Capture on the egress interface. If you see it there, PF passed it. If you see it on ingress but not egress, PF dropped it (or the kernel routed it elsewhere — check the routing table).
- “What is the server actually sending?” Capture on the egress interface facing the server. The frames you see are the ones the kernel forwarded to the server.
- “What does the client see?” Capture on the ingress interface from the client. The frames you see are the ones the client (or its switch) sent.
Reading the timestamp
The first field in a tcpdump line is the timestamp. By default, tcpdump prints seconds since the epoch with microsecond precision (e.g. 12:34:56.789012). The -ttt flag prints delta times from the previous packet; -ttttt prints delta from the first packet. The -j adapter time can give nanosecond precision on supported drivers.
The discipline:
- Use absolute timestamps for cross-reference. When correlating a capture with a firewall log, the timestamp must be absolute (and in the same timezone — typically UTC on OPNsense).
- Use delta timestamps for flow analysis. When characterising a TCP flow, the delta from the SYN to the SYN-ACK is the connection latency. The delta from the last data packet to the FIN is the close latency.
Summary
- Packet capture on OPNsense uses libpcap, which receives a copy of every frame in parallel with the kernel’s normal processing.
- The capture is a witness, not an actor — PF does not know the capture is happening and the capture cannot show PF’s decision.
- Capture proves a frame was on the wire; the firewall log proves what PF decided; the state table proves what was matched.
- Choose the capture interface based on the question: ingress for “did it arrive”, egress for “did it leave”.
- Filter at the BPF layer — userspace filtering is wasteful.
Knowledge check · 4 questions
Q1. A capture on the LAN interface shows the SYN of an outbound connection. No corresponding packet appears on a simultaneous capture of the WAN interface. The firewall log shows no block. What is the most likely explanation?
Q2. A packet capture on the egress interface is sufficient evidence that PF allowed the packet through.
Q3. Which of the following can be determined from a packet capture alone? Select all that apply.
Q4. You want to confirm that a packet arriving on the LAN interface is being forwarded to the WAN. Where should you capture?
Passing score: 75%. Answers are checked in this browser.