The only way to really debug a dropped packet in nftables
By Saket Jain Published Linux/Unix
The only way to really debug a dropped packet in nftables
Technical Briefing | 11/12/2026
You’ve spent hours crafting your nftables rules. You’ve checked every IP, every port. You’re sure it’s correct. And yet, traffic isn’t flowing. Your service is unreachable. What gives? Most people reach for `tcpdump` or throw a `log` rule in, hoping for a clue. But sometimes, even that’s not enough. The packet arrived, but where did it die? That’s when you need to go deeper, into the kernel’s decision-making process itself.
When `LOG` just lies to you
Let’s be honest, `log` rules are handy for a quick sanity check. They tell you a packet *hit* a rule, sure. But they don’t tell you *why* it got dropped later in another chain, or if a subsequent rule overrode the one you expected to trigger, or if it hit the default policy after a convoluted journey. I’ve wasted too many hours trying to piece together a packet’s complete journey from scattered `syslog` entries. It’s like trying to navigate a dark maze with only a flicker of a match; you see parts, but never the whole path.
Following the breadcrumbs with `nft trace`
The kernel actually has an incredibly powerful debugging mechanism for network packets, built right into nftables. It’s called `trace`, and it’s how you get a blow-by-blow account of exactly which rules a packet matched, which chains it traversed, and what verdict was rendered at each step. This isn’t just about ‘did it hit this rule?’. It’s about ‘what was the final decision for this packet, and *why*?’ It’s a lifesaver when you have complex interdependencies or unexpected behaviors.
nft add rule ip filter input tcp dport 80 meta nftrace set 1
# Now, try to establish the connection that's failing, e.g., curl your-server-ip/
dmesg | grep 'nftrace'
nft delete rule ip filter input tcp dport 80 meta nftrace set 1
That `dmesg` output is where the magic happens. You’ll see lines like `trace id 137a679e ip filter input hook input policy accept type filter prior 0` followed by specific rule information. Each line shows you the rule ID, the chain it entered, any matches, and the verdict (`accept`, `drop`, `jump`, `return`). Pay close attention to the `type filter` and `hook input` parts – they tell you precisely where in the packet processing pipeline the packet is being evaluated. If a packet gets `drop`ped early, you’ll see that verdict and the specific rule ID responsible. If it traverses many rules and then gets silently dropped by a default policy, you’ll see it hit the end of a chain without an explicit accept. This level of detail is something no amount of `tcpdump` or `log` lines can give you.
- Be specific with your trace rules. `nftrace` can be *incredibly* chatty, so target the traffic you actually care about to avoid drowning your `dmesg` buffer.
- Always remember to `delete` your trace rules. Leaving them in place, especially on a busy system, can introduce performance overhead.
- The trace output clearly shows the *final* verdict for each rule, which is invaluable for understanding how `jump` or `goto` statements truly affect packet flow.
- This tool operates at a deeply granular level, showing you what the kernel is *thinking* about the packet’s fate, not just its wire presence.
Look, `nftrace` isn’t something you’ll fire up every day. It’s not a general-purpose monitoring tool, and it’s definitely not for casual firewall checks. But when you’re staring at a service that just won’t connect, and every `ping` and `telnet` says ‘nope’ while your rules look fine, `nft trace` is your secret weapon. It cuts through the guesswork and tells you exactly which kernel decision killed your packet. Learn it, stash it in your mental toolkit, and you’ll be profoundly glad you did when the truly baffling firewall issues land on your plate.
