T Techclick ← All lessons
Fortinet · FortiGate · Interactive lesson

debug flow filter then one packet

Ticket: HTTP from lab host 10.20.20.101 to 192.0.2.80:80 dies. Three policies look right. Do not clone a fourth. Filter diagnose debug flow, turn on function names, reproduce one packet, paste the func= line that allowed or denied it, then turn debug off. The traffic log is history. The session table is live. Flow is why this packet died.

16 min read · L2 primary · Quiz at end

After this page you can

Lessons · FortiGate troubleshooting · debug flow

This page vs the evidence desk

This lesson is the per-packet instrument: filter, function names, one reproduce, then disable. The three-tool desk (session / flow / Log & Report) and the session-table NAT page are linked at the bottom. Do not skip the stop.

Evidence desk · debug flow + session · Policy first match

Hero · one packet through the box
LAN host sending one HTTP packet through a FortiGate to a web destination
Mood, not a wiring diagram. Lab 5-tuple is 10.20.20.101 → 192.0.2.80:80 (TCP). Exact CLI and the function-name path are in the SVG below. Artwork labels are not a substitute for diagnose debug flow filter.
Quick answer

Filter first. Then diagnose debug flow show function-name enable, diagnose debug enable, diagnose debug flow trace start 100. Reproduce one packet from 10.20.20.101 to 192.0.2.80:80. The proof line is func=fw_forward_handler with Allowed by Policy-N or a deny / implicit-policy drop. Then trace stop, diagnose debug disable, diagnose debug reset. A live session is diagnose sys session filter then list. A closed conversation is Log & Report → Forward Traffic (action + policyid).

Why the packet died in chat

The ticket is always the same: “HTTP is down, add an accept.” Three engineers already have three policies. None of them has a line that says which policy FortiOS actually used, or that it used none (implicit deny, policy 0).

diagnose debug flow is not a sniffer and not a log search. It prints the FortiOS decision path for the next N packets that match your filter. Fortinet documents that it generates a significant amount of data. If the box ships logs to FortiAnalyzer or FortiCloud, that output is recorded as event messages. Unfiltered flow on a busy production box is a change you did not get approved.

Do not start with enable

Fortinet’s own sample happens to run diagnose debug enable then immediately filter. On a live box, arm the filter before you enable. Never trace start 10000 with an empty filter. Ctrl+C does not stop flow debug — you type diagnose debug flow trace stop and diagnose debug disable.

Three clocks — flow, session, log

Pre-train the three instruments before the CLI. You will use flow tonight. You will confirm with one of the other two. They are not substitutes.

Debug flow — this packet

Per-packet verdict. Proof: received a packet, allocate a new session, Allowed by Policy-N, or silence. Filter then start N, then stop.

Session table — live now

diagnose sys session filter then list. Proof: proto_state, policy_id, orig/reply tuple, NAT hooks. Filter is not a list.

Traffic log — already happened

Log & Report → Forward Traffic. type=traffic subtype=forward. Proof: action, policyid, sessionid, and utmaction if a profile touched it.

They disagree on purpose

A session is state. A flow trace is why this packet. A log is history. iprope “predicted policy” is none of the three — do not paste it as live proof.

Path · filter is Path A, flood is Path B
Decision diamond splitting a filtered single packet from an unfiltered flood
Feel of the split. Path A is a filtered 5-tuple. Path B is unfiltered flow — CPU, FortiAnalyzer event flood, and a night shift that inherits a dying box. Exact filter keys are in the next SVG.
Say this out loud

Flow is the next matching packets. Session is the live 5-tuple. Traffic log is the closed conversation. Filter is not a list. Disable is part of the change.

Filter then one packet

Flowchart first. The only legal start is a filter. The only legal end is disable.

Flow 1 · filter then one packet
reset + clear no leftover filter flow filter saddr · daddr · dport function-name show … enable enable + start N trace start 100 one packet 10.20.20.101 func= line? Policy-N / deny YES stop · disable · reset always — even if empty NO / silence wrong VDOM / NP then still disable Lab 5-tuple: 10.20.20.101 → 192.0.2.80:80 proto 6 IPv4 keys: addr · saddr · daddr · port · sport · dport · proto · vd · clear GUI twin: Network → Diagnostics → Debug Flow. Stop is a click, not a hope.

Read left → right, then down. Yellow is the reproduce. Green is the close. Red silence is still a close — then check VDOM or NP offload, do not leave flow running.

How to choose the instrument

If you seeFirst toolProof fieldNot first
This packet is dying right now; you have src/dst/portFiltered debug flowfunc= + Allowed / Denied lineCloning policy 15
HTTPS hangs; a session should existSession table: filter then listproto_state + policy_idUnfiltered flow
User said “blocked” ten minutes ago; session goneLog & Report → Forward Trafficaction + policyidDebug enable
Policy already accepts; page is a FortiGuard / IPS blockTraffic log + Security Eventsutmaction (and utmref)Another accept
Filtered flow is silent on live HTTPS (hardware)Session npu_state, then ICMP flow or disable NP offload for the testOfficial NP6/NP7 exceptionLeaving flow on overnight

Primary source for the flow column: FortiOS Administration Guide — Debugging the packet flow, plus Using the debug flow tool. Primary source for the session column: Using a session table. Primary source for the log column: FortiOS Log Message Reference — log message fields.

Runbook Side A / B / C

Lab only: host 10.20.20.101 → dest 192.0.2.80:80 TCP. Confirm live syntax, VDOM, and change-control before you type this on a real box. Every step cites one official page.

Side A — filter (CLI + GUI)

Source: Debugging the packet flow — diagnose debug flow {filter | filter6} <option>. IPv4 keys: addr, saddr, daddr, port, sport, dport, proto, vd (−1 matches all), negate, clear.

  1. State VDOM and leftover debug

    get system status. If VDOMs are on, enter the tenant VDOM first. Then diagnose debug reset and diagnose debug flow filter clear so you do not inherit last night’s addr.

  2. Arm the 5-tuple — this is not a start

    diagnose debug flow filter saddr 10.20.20.101 then daddr 192.0.2.80 then dport 80 then proto 6. Or one-shot filter addr 10.20.20.101 if that host talks only to the lab dest. Display the filter with a bare diagnose debug flow filter.

  3. GUI twin

    Network → Diagnostics → Debug Flow. Basic filter = addr / port / proto. Advanced = saddr / sport / daddr / dport / proto. Same keys as CLI.

https://192.0.2.10/#/network/diagnostics/debug-flow
Training mock · not live

Network › Diagnostics › Debug Flow

Debug Flow — Advanced filter

10.20.20.101
192.0.2.80
80
6 · TCP

Source: FortiOS Administration Guide — Using the debug flow tool. Path: Network → Diagnostics → Debug Flow. Next click after the reproduce: Stop debug flow, then Save as CSV if you need the file. Add the Function column (Configure Table) to see fw_forward_handler next to the message. Hardware note on that page: NPU offload can hide packets — disable acceleration on the test policy if the GUI is empty.

Side B — product (function names, enable, one packet)

Source: Debugging the packet flow. Fortinet’s documented example:

Official sample shape — then our lab 5-tuple
diagnose debug enable
diagnose debug flow filter addr 203.160.224.97
diagnose debug flow show function-name enable
diagnose debug flow trace start 100

On the lab box, put the filter on first. Function names are the column that makes the dump readable.

  1. Show function names

    diagnose debug flow show function-name enable. Optional and useful: diagnose debug flow show iprope enable (which internal policy / iprope check) and diagnose debug console timestamp enable.

  2. Start a bounded trace, then enable

    diagnose debug flow trace start 100 then diagnose debug enable. One hundred lines is enough for one HTTP GET. Do not start 99999 “just in case.”

  3. Reproduce once

    From 10.20.20.101: curl -sS -D- --max-time 5 http://192.0.2.80/. Watch for id=20085 trace_id=… func=print_pkt_detail then the verdict line.

  4. Read the official-style lines

    Look for received a packet, allocate a new session, find a route, Allowed by Policy-N: SNAT, or deny / implicit policy. Silence after a reproduce is also evidence — wrong VDOM, packet never arrived, or NP offload.

Lab sequence — filter, show, one packet, off
diagnose debug reset
diagnose debug flow filter clear
diagnose debug flow filter saddr 10.20.20.101
diagnose debug flow filter daddr 192.0.2.80
diagnose debug flow filter dport 80
diagnose debug flow filter proto 6
diagnose debug flow show function-name enable
diagnose debug flow trace start 100
diagnose debug enable
# curl once from 10.20.20.101 to http://192.0.2.80/
id=20085 trace_id=17 func=print_pkt_detail msg="vd-root:0 received a packet(proto=6, 10.20.20.101:51990->192.0.2.80:80) from port2."
id=20085 trace_id=17 func=resolve_ip_tuple msg="allocate a new session-00000e90"
id=20085 trace_id=17 func=fw_forward_handler msg="Denied by implicit policy"
diagnose debug flow trace stop
diagnose debug disable
diagnose debug reset
What the sample lines mean

Fortinet’s HTTP sample starts with func=resolve_ip_tuple_fast / print_pkt_detail and vd-root received a packet(proto=6, …). Community traces add __iprope_check_one_policy (policy-N is matched, act-accept) and fw_forward_handler (Allowed by Policy-N: SNAT). Policy 0 / implicit deny means no forward policy matched the ingress/egress pair — not “logging is off.”

Side C — proof (session vs traffic log, then disable)

Source: Using a session table; Log message fields. Flow told you why this packet. Confirm on the matching clock, then kill debug.

  1. If the packet installed a session

    diagnose sys session filter src 10.20.20.101 then dst 192.0.2.80 then dport 80 then list. Quote policy_id and proto_state. TCP proto_state=01 is SYN seen, not established. 11 is the established pair operators look for after a fix. Filter is not a list.

  2. If it already ended

    Log & Report → Forward Traffic. Filter src 10.20.20.101, dst 192.0.2.80. Proof: action (accept / deny / timeout) + policyid. If a security profile hit, also utmaction. A traffic-log accept is not a finished handshake.

  3. Stop is part of isolate

    diagnose debug flow trace stop then diagnose debug disable then diagnose debug reset. GUI: Stop debug flow. Confirm no leftover filter if you will hand the box to day shift.

Green success

One packet after enable

After trace start + diagnose debug enable, one TCP SYN from the lab host does this:

Flow 2 · this packet through FortiOS
Receive print_pkt_detail New session? resolve_ip_tuple Route vf_ip_route_input Policy? iprope / fw_forward Allow + SNAT Policy-N · session Implicit deny policyid=0 · no session After the packet: session table is now; traffic log is later Allow installs a session (proto_state starts at 01 for TCP SYN). Deny often leaves the table empty. When the conversation ends, Forward Traffic writes action + policyid. That row is not a live handshake. Existing session skips a new policy lookup until timeout / clear / dirty. Flow on a later packet says “Find an existing session”.

A policy edit that “did nothing” is usually a stale session, not a missing accept. Clear only the filtered 5-tuple after you meant to, never a global session flush on production.

Existing session vs new SYN

If flow prints Find an existing session, you are not watching a policy lookup. You are watching state. To re-judge after a policy change: filter the session, clear that filter (not the whole table), then flow the next SYN.

Traps + proof

Proof · debug off is the last line in the ticket
Operations desk with health checks and a note that debug is off
Artwork. The close is a quiet console: trace stop, diagnose debug disable, diagnose debug reset. A pasted Policy-N line with debug still running is an incomplete change.
SymptomLikely causeProof / fix
CPU / FAZ event flood after “quick debug”No filter, or leftover filter + enable.filter clear, then saddr/daddr. Fortinet: significant data; FAZ records it.
Night shift inherits a dying boxForgot disable. Ctrl+C does not stop flow.trace stopdiagnose debug disablereset.
Policy edit did nothingStale session still matching old policy_id.Session filter + list. Clear only that 5-tuple. Then flow the next SYN.
Flow Allowed, user still brokenAsymmetry / return path / server. TCP proto_state=01.Session reply-tuple and routing. Do not add another accept.
Denied by implicit policy / policyid=0No forward policy for that ingress/egress pair.Fix the matching policy (intf + addr + service). Do not disable the implicit deny.
Traffic log action=accept, page is a blockUTM profile. Firewall accepted; IPS/AV/Web Filter did not.utmaction on the same sessionid. Not a missing allow.
Filtered HTTPS flow is empty; session existsNP6/NP7 offload. Official GUI warning.Read npu_state. Test with ICMP, or disable NPU on the lab policy. Do not leave flow on.
Empty flow, empty sessionPacket never hit this VDOM / this box.Sniffer on the ingress intf. Check HA role — empty table on standby is not a missing policy.
Pilot checklist

Knowledge check

Six judgment calls on filter, one packet, and the three clocks — not menu trivia.

Q1

HTTP from 10.20.20.101 to 192.0.2.80:80 dies. A junior types diagnose debug enable then diagnose debug flow trace start 10000 with no filter. First problem?

Correct: b. Filter first. Re-read Why the packet died and Side A.
Q2

User was blocked ten minutes ago. Live diagnose sys session list for that src is empty. Debug flow now prints nothing. First instrument?

Correct: c. Session is live now; traffic log is history. Re-read Three clocks and How to choose.
Q3

You need the internal function name next to each line (print_pkt_detail, fw_forward_handler). Official command?

Correct: a. Fortinet’s documented trio is filter, show function-name enable, trace start, with diagnose debug enable. Re-read Side B.
Q4

You have the Denied by implicit policy line. Mandatory close?

Correct: b. Stop is part of isolate. Re-read Side C and Traps.
Q5

Hardware FortiGate. Session exists for the HTTPS 5-tuple. Filtered debug flow is silent. Official next thought?

Correct: d. Fortinet’s GUI debug-flow page warns that NPU acceleration must be disabled on the policy to see all packets. Re-read How to choose and Traps.
Q6

Flow prints Allowed by Policy-14: SNAT. User still broken. Session proto_state=01. What is true?

Correct: b. TCP 01 is SYN seen. Flow allowed; the session is not established. Re-read Side C and Flow 2.

Sources

Related: debug flow + session table · Evidence desk — first tool, one field · Policy first match · Troubleshooting command center · SNAT vs VIP