Counters, logs and traces
When something is blocked and you don't know why, three tools tell you which rule did what.
Counters
A counter statement counts the packets and bytes that reach it. Put one on the final line of each chain (just before the policy), and the question "is my firewall dropping this?" becomes "is that number going up?". nft list ruleset shows the values; reloading the ruleset resets them.
Logging
A log statement writes a line to the kernel log, readable with journalctl -k (or dmesg), and then lets the packet carry on to the next rule. Always give it a prefix to find the lines, and a limit: a log rule without one turns a flood of packets into a flood of log lines. The router logs at most 6 dropped packets a minute per chain, after a burst of 5. Each line shows the interfaces, addresses, ports and TCP flags, which is usually enough to see why a packet didn't match.
In a container lab there's one catch: the kernel only logs for network namespaces other than the host's when net.netfilter.nf_log_all_netns is 1, a host-wide setting. On a normal server you'll never see this.
Tracing a packet
nft monitor trace shows a packet's whole journey: every chain it enters, every rule that matches and the final verdict. Tracing only covers packets marked with meta nftrace set 1, so first add a rule that marks the packets you care about, as early as possible: here, a temporary chain on the prerouting hook at a priority just before conntrack (raw - 1), matching the neighbour's SYNs.
The trace for the neighbour's dropped connection reads like a story: it passes the trace chain, NAT prerouting leaves it alone (it's not for port 8080), it enters the forward chain, matches no accept rule, is logged, counted, and dropped by the policy. Delete the tracing chain afterwards; it costs a little on every packet.
root@fw:~# journalctl -k --no-pager -o short-iso | grep "nft forward drop"
2026-10-21T07:47:59+00:00 fw kernel: nft forward drop: IN=eth0 OUT=eth1 MAC=6a:11:8d:11:a4:35:ae:08:ff:c3:3e:c8:08:00 SRC=203.0.113.50 DST=10.10.0.80 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=26349 DF PROTO=TCP SPT=37372 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0
2026-10-21T07:48:00+00:00 fw kernel: nft forward drop: IN=eth0 OUT=eth1 MAC=6a:11:8d:11:a4:35:ae:08:ff:c3:3e:c8:08:00 SRC=203.0.113.50 DST=10.10.0.80 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=26350 DF PROTO=TCP SPT=37372 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0
2026-10-21T07:48:01+00:00 fw kernel: nft forward drop: IN=eth0 OUT=eth1 MAC=6a:11:8d:11:a4:35:ae:08:ff:c3:3e:c8:08:00 SRC=203.0.113.50 DST=10.10.0.80 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=26351 DF PROTO=TCP SPT=37372 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0
2026-10-21T07:48:10+00:00 fw kernel: nft forward drop: IN=eth0 OUT=eth1 MAC=6a:11:8d:11:a4:35:ae:08:ff:c3:3e:c8:08:00 SRC=203.0.113.50 DST=10.10.0.80 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=13601 DF PROTO=TCP SPT=36952 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0
2026-10-21T07:48:11+00:00 fw kernel: nft forward drop: IN=eth0 OUT=eth1 MAC=6a:11:8d:11:a4:35:ae:08:ff:c3:3e:c8:08:00 SRC=203.0.113.50 DST=10.10.0.80 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=13602 DF PROTO=TCP SPT=36952 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0
2026-10-21T07:48:12+00:00 fw kernel: nft forward drop: IN=eth0 OUT=eth1 MAC=6a:11:8d:11:a4:35:ae:08:ff:c3:3e:c8:08:00 SRC=203.0.113.50 DST=10.10.0.80 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=13603 DF PROTO=TCP SPT=36952 DPT=80 WINDOW=64240 RES=0x00 SYN URGP=0 - Lines 2–4: The neighbour's first attempt: a SYN and two retries, logged with interfaces, addresses, ports and flags.
- Lines 5–7: Its second attempt, ten seconds later.
root@fw:~# nft add chain inet filter trace_pre "{ type filter hook prerouting priority raw - 1; }" && nft add rule inet filter trace_pre ip saddr 203.0.113.50 tcp flags syn meta nftrace set 1
root@inet:~# curl -s -o /dev/null -w "%{http_code}\n" -m 2 http://10.10.0.80/
000
root@fw:~# nft delete chain inet filter trace_pre- Line 1: A temporary chain that marks the neighbour's SYNs, before connection tracking runs.
- Lines 3–4: The packet to follow.
- Line 6: Remove it afterwards.
root@fw:~# timeout 6 nft monitor trace
trace id 5ba96536 inet filter trace_pre packet: iif "eth0" ether saddr ae:08:ff:c3:3e:c8 ether daddr 6a:11:8d:11:a4:35 ip saddr 203.0.113.50 ip daddr 10.10.0.80 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 61905 ip protocol tcp ip length 60 tcp sport 57192 tcp dport 80 tcp flags == syn tcp window 64240
trace id 5ba96536 inet filter trace_pre rule ip saddr 203.0.113.50 tcp flags syn meta nftrace set 1 (verdict continue)
trace id 5ba96536 inet filter trace_pre policy accept
trace id 5ba96536 ip nat prerouting packet: iif "eth0" ether saddr ae:08:ff:c3:3e:c8 ether daddr 6a:11:8d:11:a4:35 ip saddr 203.0.113.50 ip daddr 10.10.0.80 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 61905 ip length 60 tcp sport 57192 tcp dport 80 tcp flags == syn tcp window 64240
trace id 5ba96536 ip nat prerouting policy accept
trace id 5ba96536 inet filter forward packet: iif "eth0" oif "eth1" ether saddr ae:08:ff:c3:3e:c8 ether daddr 6a:11:8d:11:a4:35 ip saddr 203.0.113.50 ip daddr 10.10.0.80 ip dscp cs0 ip ecn not-ect ip ttl 63 ip id 61905 ip protocol tcp ip length 60 tcp sport 57192 tcp dport 80 tcp flags == syn tcp window 64240
trace id 5ba96536 inet filter forward rule limit rate 6/minute burst 5 packets log prefix "nft forward drop: " (verdict continue)
trace id 5ba96536 inet filter forward rule counter packets 3 bytes 180 comment "dropped by the policy" (verdict continue)
trace id 5ba96536 inet filter forward policy drop
[... 9 lines trimmed: the retransmitted SYN, traced and dropped the same way ...]- Lines 2–4: The marking chain.
- Lines 5–6: NAT prerouting: no rule matches, so no DNAT.
- Line 7: The forward chain sees it, after routing (TTL now 63).
- Lines 8–10: No accept rule matches: logged, counted, then the policy drops it.
Check your understanding
Why should a log rule almost always have a limit?
- Otherwise a flood of packets becomes a flood of log lines, which can fill the disk and hide everything else.
- Because log rules drop packets once the limit is reached.
- nftables requires it.
- To make the counter accurate.