Course menu

Module 2: Firewall PoliciesLesson 2.4 (4 of 5 in this module)9 of 18 in the FortiGate Administrator course

Which policy did it hit?

Policy lookup, traffic logs, the session table and debug flow, to explain why traffic is allowed or blocked.

Intermediate · 12 min read

What you will learn

After this lesson, you can use policy lookup, traffic logs, the session list, the packet sniffer and debug flow to prove which policy a connection uses, or why it is dropped.

  • Policy lookup
  • Session list
  • debug flow
  • Reading the output

Debug flow is the FortiGate's packet-processing trace. For packets that match a filter, it prints each decision the FortiGate makes: the packet arriving, a new or existing session, the route found, the policy check, NAT and the outgoing interface. It is the most direct way to see why traffic is allowed or dropped.

In simple terms: Debug flow makes the FortiGate explain itself, step by step, for the traffic you choose.

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 list

Always clear old filters first. Filters combine: here, sessions from PC1 to port 443.

Example output · based on Fortinet documentation; exact format varies by model and FortiOS version
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
proto=6 is TCP and proto_state=01 means established. The snat line shows PC1 translated to 203.0.113.2, and policy_id=1 names the policy. (Example output, trimmed.) For the failing SSH, the same command with dport 22 returns no session at all.

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 enable

Set a filter, a packet count (10), then enable output. Reproduce the problem (PC1 tries SSH), read the trace, then stop with diagnose debug disable.

Example output · based on Fortinet documentation; exact format varies by model and FortiOS version
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)"
Read it top to bottom: the SYN arrived on port2, a route via port1 exists, and then the policy check found no match, so the implicit deny (policy 0) dropped it. The fix is a policy for SSH, not a route. (Line numbers and other fields removed.)

For an allowed connection, the policy line reads differently:

Example output · based on Fortinet documentation; exact format varies by model and FortiOS version
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 disable

Always stop debug output when finished.

5. The packet sniffer

diagnose sniffer packet any 'host 198.51.100.80 and port 22' 4

Captures 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

  1. Does the traffic arrive? Sniffer on the incoming interface.
  2. What does the FortiGate decide? Debug flow: route found? policy allowed or denied? NAT?
  3. Which policy should it be? Policy Lookup, then fix the policy or its order.
  4. 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 disable and 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

✅ 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

Predict · scenario 1

Debug flow shows "find a route … via port1" then "Denied by forward policy check (policy 0)". What is the fix?

Predict · scenario 2

The sniffer on FGT1 shows nothing from PC1 when it tries to connect. Where is the problem?

Predict · scenario 3

Which line in a session entry tells you which policy allowed it?

FAQ

Is debug flow safe on a busy production FortiGate?
Yes, if you always set a filter and a packet count (trace start 10 or 20). Without a filter it traces every packet and can load the CPU. Always end with diagnose debug disable.
Why don't I see later packets of a connection in debug flow?
Once a session is established, many packets are handled by the fast path or offloaded to hardware and no longer pass through the traced code. Start the trace before the connection begins to see its first packets.