Skip to main content
Ted
Staff
Staff
July 23, 2025

Technical Tip: FortiGate using 4.x kernel sending out ICMP TTL exceeded packet without checking Firewall Policies (Known Issue)

  • July 23, 2025
  • 0 replies
  • 1140 views
Description

This article describes a known issue where the FortiGate always transmits ICMP 'Time to Live exceeded in transit' messages in response to incoming packets with a TTL of 1, even when Firewall Policies exist that should deny this traffic (and thus prevent the ICMP response from occurring). This issue notably affects the FortiGate-VM, but also applies to newer FortiGates like NP7-based models.

Scope FortiGate, ICMP.
Solution

When receiving incoming packets with an IPv4 time-to-live (TTL) of 1, the expected behavior as defined by RFC 1812 is for the network device to drop the packet and send an ICMP TTL exceeded packet back towards the source. On the FortiGate, this behavior is further controlled by Firewall Policies, where the expected behavior is based on the action of the matching Firewall Policy:

  • If allowed, the packet with TTL=1 is dropped, and an ICMP TTL exceeded packet will be sent to the source.
  • If denied, the packet with TTL=1 is dropped, and no ICMP reply is sent out.

 

While this behavior is true for older FortiGate models running the older 3.x Linux kernel (such as the FortiGate-601E and 1500D, as well as the desktop SOC4 units like the FortiGate-60F), it was observed that FortiGates running the newer 4.x kernel (such as FortiGate-VMs and NP7 FortiGates) were not checking the Firewall Policies and were always replying with ICMP TTL exceeded packets after receiving packets with TTL=1. To check the current Linux kernel version of the FortiGate, use the command fnsysctl cat /proc/version.

 

This issue is being tracked as part of Issue #1171392 and will be fixed in FortiOS v7.4.10, v7.6.4, and the upcoming v8.0. The fix will align the behavior to how it was for the earlier 3.x kernel, where the FortiGate only sends an ICMP TTL exceeded response if the traffic would normally match a policy with the allow action. Below are examples of the observed issue:

 

Example 1: 

In this example, a FortiGate-601E has been configured with a Firewall Policy to deny all traffic, and traceroute is used here with the -T flag to send twenty TCP SYN-based probes from the client through the FortiGate. When the FortiGate receives the TCP SYN with TTL=1, it receives and drops the incoming SYN packet but does not reply to the client with 'icmp: time exceeded in-transit' (this is the expected behavior).


root@ted:/home/ted/Desktop# traceroute -n -w 1 -m 20 -T -p 7999 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 20 hops max, 60 byte packets
1 * * *
2 * * *
3 * * *
[...]
18 * * *
19 * * *
20 * * *


FortiGate-601E # diagnose sniffer packet any 'none' 4 0 l
interfaces=[any]
filters=[none]
2025-06-16 23:05:35.513460 port2 in 10.0.0.2.35729 -> 8.8.8.8.7999: syn 3444695382
2025-06-16 23:05:35.513935 port2 in 10.0.0.2.52325 -> 8.8.8.8.7999: syn 3413026689
2025-06-16 23:05:35.514087 port2 in 10.0.0.2.44621 -> 8.8.8.8.7999: syn 1978811126

 

Example 2: 

On the same FortiGate-601E, the policy has been modified to allow this TCP traffic through. The FortiGate drops the first TCP traceroute since the TTL has expired, and it does respond to the source with 'icmp: time exceeded in-transit'.


root@ted:/home/ted/Desktop# traceroute -n -w 1 -m 20 -T -p 7999 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 20 hops max, 60 byte packets
1 10.0.0.253 1.252 ms 0.974 ms 0.779 ms
2 * * *
3 * * *
[...]
18 * * *
19 * * *
20 * * *


FortiGate-601E # diagnose sniffer packet any 'none' 4 0 l
interfaces=[any]
filters=[none]
2025-06-16 23:04:09.771317 port2 in 10.0.0.2.56717 -> 8.8.8.8.7999: syn 3723035076
2025-06-16 23:04:09.771558 port2 in 10.0.0.2.41577 -> 8.8.8.8.7999: syn 3779426166
2025-06-16 23:04:09.771701 port2 in 10.0.0.2.56759 -> 8.8.8.8.7999: syn 3762780160
2025-06-16 23:04:09.771803 port2 out 10.0.0.253 -> 10.0.0.2: icmp: time exceeded in-transit
2025-06-16 23:04:09.771804 port2 out 10.0.0.253 -> 10.0.0.2: icmp: time exceeded in-transit
2025-06-16 23:04:09.771805 port2 out 10.0.0.253 -> 10.0.0.2: icmp: time exceeded in-transit

 

Example 3: 

This example is similar to Example 1, but the FortiGate-601E has been replaced with a FortiGate-VM (which uses the newer 4.x kernel) running FortiOS 7.4.8. Despite having a deny-all policy present, the FortiGate-VM drops the incoming packet with TTL=1 and does respond with an 'icmp: time exceeded in-transit' message:


root@ted:/home/ted/Desktop# traceroute -n -w 1 -m 20 -T -p 7999 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 20 hops max, 60 byte packets
1 10.0.1.253 0.750 ms 0.699 ms 0.681 ms
2 * * *
3 * * *
[...]
18 * * *
19 * * *
20 * * *


FortiGate-VM64 # diagnose sniffer packet any 'none' 4 0 l
Using Original Sniffing Mode
interfaces=[any]
filters=[none]
2025-06-17 19:31:38.990202 port1 in 10.0.1.11.33881 -> 8.8.8.8.7999: syn 4179144013
2025-06-17 19:31:38.990220 port1 in 10.0.1.11.44383 -> 8.8.8.8.7999: syn 2806653660
2025-06-17 19:31:38.990241 port1 in 10.0.1.11.33543 -> 8.8.8.8.7999: syn 2570957358
2025-06-17 19:31:38.990281 port1 out 10.0.1.253 -> 10.0.1.11: icmp: time exceeded in-transit
2025-06-17 19:31:38.990306 port1 out 10.0.1.253 -> 10.0.1.11: icmp: time exceeded in-transit
2025-06-17 19:31:38.990335 port1 out 10.0.1.253 -> 10.0.1.11: icmp: time exceeded in-transit