A real-life situation
An administrator on PC1 (10.0.1.10) reports that SSH to an external server, 198.51.100.80, times out, while web browsing works. Is the FortiGate blocking it, and if so, which policy? Guessing wastes time; these tools answer the question in a few minutes.
1. Policy Lookup (GUI)
Policy & Objects › Firewall Policy › Policy Lookup. Enter the incoming interface (port2), protocol (TCP), source (10.0.1.10), destination (198.51.100.80) and port (22). The FortiGate highlights the policy that would match, or says none does. It answers "what should happen" without needing real traffic.
2. Forward traffic logs
Log & Report › Forward Traffic, filtered by source 10.0.1.10 and destination port 22. Each entry shows the action and the policy ID. Drops by the implicit deny only appear if "Log Violation Traffic" is on, or if you have a logged deny-all policy.
3. The session list
diagnose sys session filter clear
diagnose sys session filter src 10.0.1.10
diagnose sys session filter dport 443
diagnose sys session listAlways clear old filters first. Filters combine: here, sessions from PC1 to port 443.
FGT1 # diagnose sys session list session info: proto=6 proto_state=01 duration=14 expire=3585 timeout=3600 ... ... orgin->sink: org pre->post, reply pre->post dev=5->3/3->5 gwy=203.0.113.1/10.0.1.10 hook=post dir=org act=snat 10.0.1.10:51544->198.51.100.80:443(203.0.113.2:51544) hook=pre dir=reply act=dnat 198.51.100.80:443->203.0.113.2:51544(10.0.1.10:51544) ... misc=0 policy_id=1 ... total session 1
4. Debug flow
diagnose debug reset
diagnose debug flow filter addr 198.51.100.80
diagnose debug flow filter port 22
diagnose debug flow show function-name enable
diagnose debug console timestamp enable
diagnose debug flow trace start 10
diagnose debug enableSet a filter, a packet count (10), then enable output. Reproduce the problem (PC1 tries SSH), read the trace, then stop with diagnose debug disable.
FGT1 # (PC1 tries ssh 198.51.100.80) func=print_pkt_detail msg="vd-root:0 received a packet(proto=6, 10.0.1.10:51600->198.51.100.80:22) from port2. flag [S], ..." func=init_ip_session_common msg="allocate a new session-0003a1f2" func=vf_ip_route_input_common msg="find a route: flag=04000000 gw-203.0.113.1 via port1" func=fw_forward_handler msg="Denied by forward policy check (policy 0)"
For an allowed connection, the policy line reads differently:
FGT1 # (PC1 opens https://198.51.100.80) func=fw_forward_handler msg="Allowed by Policy-1: SNAT" func=__ip_session_run_tuple msg="SNAT 10.0.1.10->203.0.113.2:51544"
diagnose debug disableAlways stop debug output when finished.
5. The packet sniffer
diagnose sniffer packet any 'host 198.51.100.80 and port 22' 4Captures matching packets on all interfaces. Verbosity 4 adds the interface name and direction, so you can see a packet arrive on port2 and (if allowed) leave on port1. Stop with Ctrl+C.
If the sniffer shows the SYN arriving on port2 but never leaving port1, the FortiGate dropped it; debug flow tells you why. If it never arrives on port2, the problem is before the FortiGate (the PC, the switch, the default gateway).
A quick method
- Does the traffic arrive? Sniffer on the incoming interface.
- What does the FortiGate decide? Debug flow: route found? policy allowed or denied? NAT?
- Which policy should it be? Policy Lookup, then fix the policy or its order.
- Is it working now? Session list and forward traffic log with the policy ID.
Why it works this way
Logs and Policy Lookup tell you what the rule base says; the sniffer and debug flow show what the FortiGate actually did with real packets. When the two disagree, the difference is the problem: a wrong route, a missing NAT, a policy in the wrong order, or traffic that never arrived.
Common mistakes
- Running debug flow without a filter or packet count on a busy firewall.
- Forgetting
diagnose debug disableand leaving debug output on. - Testing with an existing session: start a new connection so the first packet is traced.
- Filtering on the translated address instead of the real one (or the other way round). Filter on what the packet carries where it arrives.
Key takeaways
- Policy Lookup predicts the matching policy; logs and the session list show the policy ID actually used.
- The sniffer shows whether packets arrive and leave; debug flow shows why.
- "Denied by forward policy check (policy 0)" means no policy matched.
- Always filter, limit the count, and disable debug when done.
Check yourself
Debug flow shows "find a route … via port1" then "Denied by forward policy check (policy 0)". What is the fix?
The sniffer on FGT1 shows nothing from PC1 when it tries to connect. Where is the problem?
Which line in a session entry tells you which policy allowed it?