FortiGate
FortiGate Next Generation Firewall utilizes purpose-built security processors and threat intelligence security services from FortiGuard labs to deliver top-rated protection and high performance, including encrypted traffic.
Dhruvin_patel
Article Id 388379
Description This article indicates that the reason FortiGate does not send a reply to the client is that the IP Pool includes the client's IP, and arp-reply is enabled.
Scope FortiGate v6.4.16, v7.0.13, v7.2.6 and v7.4.1.
Solution

The client (10.10.10.10) initiates the traffic. FortiGate allows the traffic and also receives the reply from the server, however the FortiGate does not forward the reply to the client.

 

Sniffer shows the behavior:

 

2025-04-20 14:34:09.461964 VLAN 321 in 10.10.10.10 -> 8.8.4.4: icmp: echo request
2025-04-20 14:34:09.461993 port1 out 12.106.165.50 -> 8.8.4.4: icmp: echo request
2025-04-20 14:34:09.469598 port1 in 8.8.4.4 -> 12.106.165.50: icmp: echo reply
2025-04-20 14:34:14.162414 VLAN 321 in 10.10.10.10 -> 8.8.4.4: icmp: echo request
2025-04-20 14:34:14.162426 port1 out 12.106.165.50 -> 8.8.4.4: icmp: echo request
2025-04-20 14:34:14.169869 port1 in 8.8.4.4 -> 12.106.165.50: icmp: echo reply

 

This is because the FortiGate has an IP Pool that includes the client IP address, with arp-reply enabled.

 

config firewall ippool
   edit "NAT"
      set type overload
      set startip 10.10.10.2
      set endip 10.10.10.10
      set arp-reply enable
      set arp-intf ''
      set associated-interface ''
      set comments ''
      set nat64 disable
   next
end

 

Note:

By default, arp-reply is enabled.

 

When the arp-reply is enabled, FortiGate considers the IP included in that IP Pool as a local address: Technical Tip: ARP reply setting in Virtual IP/IP Pool

 

For this reason, the reply will be forwarded to the root, and as a result, the client will not receive the reply from the server.

The below debug flow shows the behavior.

 

Traffic initiated by the client and processed by FortiGate:

 

2025-04-20 14:39:45 id=20085 trace_id=1000 func=print_pkt_detail line=5953 msg="vd-root:0 received a packet(proto=1, 10.10.10.10:1->8.8.4.4:2048) tun_id=0.0.0.0 from VLAN 321. type=8, code=0, id=1, seq=53."
2025-04-20 14:39:45 id=20085 trace_id=1000 func=init_ip_session_common line=6133 msg="allocate a new session-000045fe, tun_id=0.0.0.0"
2025-04-20 14:39:45 id=20085 trace_id=1000 func=rpdb_srv_match_input line=1031 msg="Match policy routing id=2130771969: to 8.8.4.4 via ifindex-5"
2025-04-20 14:39:45 id=20085 trace_id=1000 func=__vf_ip_route_input_rcu line=1999 msg="find a route: flag=00000000 gw-12.106.165.49 via port1"
2025-04-20 14:39:45 id=20085 trace_id=1000 func=get_new_addr line=1227 msg="find SNAT: IP-12.106.165.50(from IPPOOL), port-60417"
2025-04-20 14:39:45 id=20085 trace_id=1000 func=fw_forward_handler line=888 msg="Allowed by Policy-24: SNAT"
2025-04-20 14:39:45 id=20085 trace_id=1000 func=ip_session_confirm_final line=3187 msg="npu_state=0x4000101, hook=4"
2025-04-20 14:39:45 id=20085 trace_id=1000 func=__ip_session_run_tuple line=3546 msg="SNAT 10.10.10.10->12.106.165.50:60417"

 

Note:

Double SNAT due to the client IP being included in the IP Pool.

 

The reply from the server is received by the FortiGate and forwarded to the root instead of the client.

 

2025-04-20 14:39:45 id=20085 trace_id=1001 func=print_pkt_detail line=5953 msg="vd-root:0 received a packet(proto=1, 8.8.4.4:60417->12.106.165.50:0) tun_id=0.0.0.0 from port1. type=0, code=0, id=60417, seq=53."
2025-04-20 14:39:45 id=20085 trace_id=1001 func=resolve_ip_tuple_fast line=6039 msg="Find an existing session, id-000045fe, reply direction"
2025-04-20 14:39:45 id=20085 trace_id=1001 func=__ip_session_run_tuple line=3559 msg="DNAT 12.106.165.50:0->10.10.10.10:1"
2025-04-20 14:39:45 id=20085 trace_id=1001 func=__vf_ip_route_input_rcu line=1999 msg="find a route: flag=80000000 gw-10.10.10.10 via root"

 

The scenario below shows when the arp-reply is disabled, and the traffic is sent directly to the client:

 

config firewall ippool
   edit "NAT"
      set startip 10.10.10.2
      set endip 10.10.10.10
      set arp-reply disable
   next
end

 

Sniffer:

 

2025-04-20 15:17:05.733839 VLAN 321 in 10.10.10.10 -> 8.8.4.4: icmp: echo request
2025-04-20 15:17:05.733877 port1 out 12.106.165.50 -> 8.8.4.4: icmp: echo request
2025-04-20 15:17:05.741296 port1 in 8.8.4.4 -> 12.106.165.50: icmp: echo reply
2025-04-20 15:17:05.741316 VLAN 321 out 8.8.4.4 -> 10.10.10.10: icmp: echo reply

 

Debug flow:

 

2025-04-20 14:40:16 id=20085 trace_id=1008 func=print_pkt_detail line=5953 msg="vd-root:0 received a packet(proto=1, 10.10.10.10:1->8.8.4.4:2048) tun_id=0.0.0.0 from VLAN 321. type=8, code=0, id=1, seq=57."
2025-04-20 14:40:16 id=20085 trace_id=1008 func=resolve_ip_tuple_fast line=6039 msg="Find an existing session, id-000045fe, original direction"
2025-04-20 14:40:16 id=20085 trace_id=1008 func=rpdb_srv_match_input line=1031 msg="Match policy routing id=2130771969: to 8.8.4.4 via ifindex-5"
2025-04-20 14:40:16 id=20085 trace_id=1008 func=__vf_ip_route_input_rcu line=1999 msg="find a route: flag=00000000 gw-12.106.165.49 via port1"
2025-04-20 14:40:16 id=20085 trace_id=1008 func=get_new_addr line=1227 msg="find SNAT: IP-12.106.165.50(from IPPOOL), port-60423"
2025-04-20 14:40:16 id=20085 trace_id=1008 func=__ip_session_run_tuple line=3546 msg="SNAT 10.10.10.10->12.106.165.50:60417"

 

Reply from server:

 

2025-04-20 14:40:16 id=20085 trace_id=1009 func=print_pkt_detail line=5953 msg="vd-root:0 received a packet(proto=1, 8.8.4.4:60417->12.106.165.50:0) tun_id=0.0.0.0 from port1. type=0, code=0, id=60417, seq=57."
2025-04-20 14:40:16 id=20085 trace_id=1009 func=resolve_ip_tuple_fast line=6039 msg="Find an existing session, id-000045fe, reply direction"
2025-04-20 14:40:16 id=20085 trace_id=1009 func=__ip_session_run_tuple line=3559 msg="DNAT 12.106.165.50:0->10.10.10.10:1"
2025-04-20 14:40:16 id=20085 trace_id=1009 func=__vf_ip_route_input_rcu line=1999 msg="find a route: flag=00000000 gw-0.0.0.0 via VLAN 321"
2025-04-20 14:40:16 id=20085 trace_id=1009 func=npu_handle_session44 line=1246 msg="Trying to offloading session from port1 to VLAN 321, skb.npu_flag=00000000 ses.state=00000204 ses.npu_state=0x04000101"
2025-04-20 14:40:16 id=20085 trace_id=1009 func=fw_forward_dirty_handler line=412 msg="state=00000204, state2=00000001, npu_state=04000101"

 

The solution is to remove the client IP from the IP Pool if it is not needed; otherwise, disable the arp-reply for the IP Pool.


Related article:

Technical Tip: IP pool and virtual IP behavior changes in FortiOS 6.4, 7.0, 7.2, and 7.4