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.
vdralio
Staff
Staff
Article Id 195448

Description

 

This article describes how to configure Hairpin NAT.

 

Hair-pinning, also known as NAT loopback, is a technique where a machine accesses another machine on the LAN or DMZ via an external network.

Traffic goes from the LAN to the WAN interface (without actually leaving the network), then is forwarded through a virtual IP to the DMZ.


hairpin_diag.png


Scope

 

FortiGate.

 

Solution

 

If the external IP belongs to FortiGate (IP address of an external interface), FortiGate will require a different set of rules when the external IP is just from range, but not directly configured on FortiGate’s interfaces. In all examples, traffic will be flowing like this: Client -> external IP -> FortiGate -> internal IP -> Server.

 

Example 1:

Configuration of VIP and two firewall policies. One for external users and the other for internal users.

 

The following is the configuration for the VIP.  The External Internal Interface 'extintf' can be configured as 'any' or specific.

 

config firewall vip
    edit "VIP1"
        set extip 20.0.0.1
        set extintf "any" or "wan"
        set portforward enable
        set mappedip "172.16.1.2"
        set extport 10443
        set mappedport 10443
     next
end

 

The following is the policy configuration.  First policy for the Internal users:

 

config firewall policy
    edit 1
        set name "AllLan1"
        set srcintf "lan"
        set dstintf "wan"
        set srcaddr "all"
        set dstaddr "all"
        set action accept
        set schedule "always"
        set service "ALL"
        set logtraffic all
        set nat enable
    next

 

The second policy, for the external users:

 

    edit 3
        set name "VIP"
        set srcintf "wan"
        set dstintf "dmz"
        set srcaddr "all"
        set dstaddr "VIP1"
        set action accept
        set schedule "always"
        set service "ALL"
        set logtraffic all
    next
end

 

If the source address 'all' is replaced by a specified source address in the WAN to DMZ policy, for example, Geo USA, the LAN subnet 192.168.1.0/24 will not be able to access the local server if the source address in the policy has only Geo USA. This is because the traffic from the LAN will be sent to the WAN interface, and FortiGate will check the policy from the WAN to the DMZ to allow traffic.

Add the LAN subnet to the source of the policy to allow the LAN subnet to access as well.

 

Example 2:

The external IP address is from the same subnet but does not belong to FortiGate directly. Use VIP2 from the diagram. There are two options to select extintf: any or specific.

 

Option 1 (extintf set to 'any'):

 

config firewall vip
    edit "VIP2"
        set extip 20.0.0.2
        set extintf "any"
        set portforward enable
        set mappedip "172.16.1.2"
        set extport 10443
        set mappedport 10443
    next
end

 

If the interface is any, there will be just one firewall policy from LAN to DMZ with VIP2 as the destination address.

 

config firewall policy
    edit 3
        set name "VIP"
        set srcintf "lan"
        set dstintf "dmz"
        set srcaddr "all"
        set dstaddr "VIP2"
        set action accept
        set schedule "always"
        set service "ALL"
        set logtraffic all
    next
end

 

Note:

  • If srcintf-filter to VIP2 is configured, the LAN port will need to be a member of that filter. In that case, the same firewall policy as the previous one will be enough. If LAN is not a member of the filter, but only WAN, hairpin will not work even if firewall policies are corrected when the srcintf is WAN directly (next option).
  • In some scenarios, network admin could have configured a policy route sending all traffic to the internet.
 
config router policy
    edit 65  <-- policy route for the internet traffic LAN to WAN.
        set input-device "lan"
        set src "192.168.1.0"
        set dst "0.0.0.0/0.0.0.0"
        set gateway 20.0.0.254
        set output-device "wan"
end

 

This can make after dnat the policy route force the traffic of mapped IP through the WAN interface instead of DMZ, resulting in policy denial.

 

1-stoppolicyrouting.png

 

To fix it, try to configure a new route policy above to stop policy routing the traffic to the mapped IP of the vip.

 

config router policy
    edit 1
        set input-device "lan"
        set src "192.168.1.0"
        set dst "172.16.1.2"  <--- mapped IP of the VIP.
        set action deny <-- To stop policy routing for this traffic.
        set comments "Stop Policy based"
end

 

 

Option 2 (extintf set to the specific interface):

With option 2, when the external IP address is from the same subnet but does not belong to FortiGate directly:
This setup uses the same set of firewall policies as in Example 1. One policy is in place to allow access from LAN to WAN, and the second policy is for WAN to DMZ.

 

Hairpin-NAT_1.png

config firewall vip

    edit "VIP"

        set uuid cf7d355c-5729-51ef-abab-952e8d72ad56

        set extip 172.16.16.10       <- External IP address does not belong to FortiGate directly.

        set mappedip "10.10.10.2"

        set extintf "port4"

    next

end

 

config firewall policy

    edit 1

        set name "Lan-to-Wan"

        set uuid bf1cdbee-574d-51ef-9242-288e63f80e1f

        set srcintf "port3"

        set dstintf "port4"
        set action accept

        set srcaddr "Lan_Subnet"

        set dstaddr "Wan_Subnet"

        set schedule "always"

        set service "ALL"

        set nat enable

    next

end

 

config firewall policy

    edit 2

        set name "Wan-to-Dmz"
        set uuid dbdfac02-574d-51ef-984d-ab53ab0d1258
        set srcintf "port4"
        set dstintf "port2"
        set action accept
        set srcaddr "Lan_Subnet"
        set dstaddr "VIP"
        set schedule "always"
        set service "ALL"
        set nat enable

    next

end

 

id=65308 trace_id=1 func=print_pkt_detail line=5895 msg="vd-root:0 received a packet(proto=1, 192.168.1.2:33214->172.16.16.10:2048) tun_id=0.0.0.0 from port3. type=8, code=0, id=33214, seq=1."
id=65308 trace_id=1 func=init_ip_session_common line=6076 msg="allocate a new session-0000035b, tun_id=0.0.0.0"
id=65308 trace_id=1 func=iprope_dnat_check line=5331 msg="in-[port3], out-[]"
id=65308 trace_id=1 func=iprope_dnat_tree_check line=823 msg="len=1"
id=65308 trace_id=1 func=__iprope_check_one_dnat_policy line=5191 msg="checking gnum-100000 policy-1"
id=65308 trace_id=1 func=get_new_addr line=1228 msg="find DNAT: IP-10.10.10.2, port-0(fixed port)"
id=65308 trace_id=1 func=__iprope_check_one_dnat_policy line=5286 msg="matched policy-1, act=accept, vip=1, flag=104, sflag=2000000"
id=65308 trace_id=1 func=iprope_dnat_check line=5343 msg="result: skb_flags-02000000, vid-1, ret-matched, act-accept, flag-00000104"
id=65308 trace_id=1 func=fw_pre_route_handler line=178 msg="VIP-10.10.10.2:33214, outdev-unknown"
id=65308 trace_id=1 func=__ip_session_run_tuple line=3515 msg="DNAT 172.16.16.10:8->10.10.10.2:33214"
id=65308 trace_id=1 func=vf_ip_route_input_common line=2605 msg="find a route: flag=04000000 gw-10.10.10.2 via port2"
id=65308 trace_id=1 func=iprope_fwd_check line=789 msg="in-[port3], out-[port2], skb_flags-020000c0, vid-1, app_id: 0, url_cat_id: 0"
id=65308 trace_id=1 func=__iprope_check line=2292 msg="gnum-100004, check-ffffffffa002c2a7"
id=65308 trace_id=1 func=__iprope_check_one_policy line=2044 msg="checked gnum-100004 policy-0, ret-matched, act-accept"
id=65308 trace_id=1 func=__iprope_user_identity_check line=1819 msg="ret-matched"
id=65308 trace_id=1 func=__iprope_check_one_policy line=2262 msg="policy-0 is matched, act-drop"
id=65308 trace_id=1 func=__iprope_check line=2309 msg="gnum-100004 check result: ret-matched, act-drop, flag-08010800, flag2-00004000"
id=65308 trace_id=1 func=iprope_fwd_check line=826 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-drop, idx-0"
id=65308 trace_id=1 func=iprope_fwd_auth_check line=845 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-drop, idx-0"
id=65308 trace_id=1 func=fw_forward_handler line=738 msg="Denied by forward policy check (policy 0)"

 

Traffic can be observed not leaving the firewall, even though the correct firewall policy is configured from the WAN to the DMZ. After matching with the VIP, the traffic is matched with 'policy 0' instead of the configured firewall policy.

 

This can also be verified by modifying the VIP configuration with an IP address that belongs to the firewall directly. After making these changes, we could observe that traffic matched the correct configured policy instead of 'policy 0'.

 

config firewall vip

    edit "VIP"

        set uuid cf7d355c-5729-51ef-abab-952e8d72ad56

        set extip 172.16.16.1         <- External IP address belongs to FortiGate directly.

        set mappedip "10.10.10.2"

        set extintf "any"

    next

end

 

With the same set of Firewall Policies used above:

 

config firewall policy

    edit 1

        set name "Lan-to-Wan"

        set uuid bf1cdbee-574d-51ef-9242-288e63f80e1f
        set srcintf "port3"
        set dstintf "port4"
        set action accept
        set srcaddr "Lan_Subnet"
        set dstaddr "Wan_Subnet"
        set schedule "always"
        set service "ALL"
        set nat enable

    next

end

 

config firewall policy

    edit 2

        set name "Wan-to-Dmz"
        set uuid dbdfac02-574d-51ef-984d-ab53ab0d1258
        set srcintf "port4"
        set dstintf "port2"
        set action accept
        set srcaddr "Lan_Subnet"
        set dstaddr "VIP"
        set schedule "always"
        set service "ALL"
        set nat enable

    next

end

 

FortiGate # id=65308 trace_id=6 func=print_pkt_detail line=5895 msg="vd-root:0 received a packet(proto=1, 192.168.1.2:43966->172.16.16.1:2048) tun_id=0.0.0.0 from port3. type=8, code=0, id=43966, seq=1."
id=65308 trace_id=6 func=init_ip_session_common line=6076 msg="allocate a new session-000003a0, tun_id=0.0.0.0"
id=65308 trace_id=6 func=iprope_dnat_check line=5331 msg="in-[port3], out-[]"
id=65308 trace_id=6 func=iprope_dnat_tree_check line=823 msg="len=1"
id=65308 trace_id=6 func=__iprope_check_one_dnat_policy line=5191 msg="checking gnum-100000 policy-1"
id=65308 trace_id=6 func=get_new_addr line=1228 msg="find DNAT: IP-10.10.10.2, port-0(fixed port)"
id=65308 trace_id=6 func=__iprope_check_one_dnat_policy line=5286 msg="matched policy-1, act=accept, vip=1, flag=104, sflag=2000000"
id=65308 trace_id=6 func=iprope_dnat_check line=5343 msg="result: skb_flags-02000000, vid-1, ret-matched, act-accept, flag-00000104"
id=65308 trace_id=6 func=iprope_fwd_check line=789 msg="in-[port3], out-[port4], skb_flags-02000000, vid-1, app_id: 0, url_cat_id: 0"
id=65308 trace_id=6 func=__iprope_check line=2292 msg="gnum-100004, check-ffffffffa002c2a7"
id=65308 trace_id=6 func=__iprope_check_one_policy line=2044 msg="checked gnum-100004 policy-1, ret-matched, act-accept"
id=65308 trace_id=6 func=__iprope_user_identity_check line=1819 msg="ret-matched"
id=65308 trace_id=6 func=get_new_addr line=1228 msg="find SNAT: IP-172.16.16.1(from IPPOOL), port-43966"
id=65308 trace_id=6 func=__iprope_check_one_policy line=2262 msg="policy-1 is matched, act-accept"
id=65308 trace_id=6 func=__iprope_check line=2309 msg="gnum-100004 check result: ret-matched, act-accept, flag-08050100, flag2-00004000"
id=65308 trace_id=6 func=iprope_fwd_check line=826 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-1"
id=65308 trace_id=6 func=iprope_fwd_auth_check line=845 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-1"
id=65308 trace_id=6 func=fw_pre_route_handler line=178 msg="VIP-10.10.10.2:43966, outdev-unknown"
id=65308 trace_id=6 func=__ip_session_run_tuple line=3515 msg="DNAT 172.16.16.1:8->10.10.10.2:43966"
id=65308 trace_id=6 func=vf_ip_route_input_common line=2605 msg="find a route: flag=04000000 gw-10.10.10.2 via port2"
id=65308 trace_id=6 func=iprope_fwd_check line=789 msg="in-[port4], out-[port2], skb_flags-020000c0, vid-1, app_id: 0, url_cat_id: 0"
id=65308 trace_id=6 func=__iprope_check line=2292 msg="gnum-100004, check-ffffffffa002c2a7"
id=65308 trace_id=6 func=__iprope_check_one_policy line=2044 msg="checked gnum-100004 policy-2, ret-matched, act-accept"
id=65308 trace_id=6 func=__iprope_user_identity_check line=1819 msg="ret-matched"
id=65308 trace_id=6 func=get_new_addr line=1228 msg="find SNAT: IP-10.10.10.1(from IPPOOL), port-43966"
id=65308 trace_id=6 func=__iprope_check_one_policy line=2262 msg="policy-2 is matched, act-accept"
id=65308 trace_id=6 func=__iprope_check line=2309 msg="gnum-100004 check result: ret-matched, act-accept, flag-08050500, flag2-00004000"
id=65308 trace_id=6 func=iprope_fwd_check line=826 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-2"
id=65308 trace_id=6 func=iprope_fwd_auth_check line=845 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-2"
id=65308 trace_id=6 func=iprope_reverse_dnat_check line=1307 msg="in-[port4], out-[port2], skb_flags-020000c0, vid-1"
id=65308 trace_id=6 func=iprope_reverse_dnat_tree_check line=915 msg="len=0"
id=65308 trace_id=6 func=fw_forward_handler line=903 msg="Allowed by Policy-2: SNAT"
id=65308 trace_id=6 func=__ip_session_run_tuple line=3502 msg="SNAT 192.168.1.2->10.10.10.1:43966"
id=65308 trace_id=7 func=print_pkt_detail line=5895 msg="vd-root:0 received a packet(proto=1, 10.10.10.2:43966->10.10.10.1:0) tun_id=0.0.0.0 from port2. type=0, code=0, id=43966, seq=1."
id=65308 trace_id=7 func=resolve_ip_tuple_fast line=5983 msg="Find an existing session, id-000003a0, reply direction"

 

During the evaluation process, the first policy performs SNAT, but since the traffic processing is not complete, the action of the second policy (the last one matched) takes precedence.
The traffic will not be SNAT-ed if there's no SNAT in the second policy ID (ID 3 in the example above). If SNAT is required, an IP pool has to be created and 'called' in the second policy that is being evaluated: Policy ID 3:

 

config firewall policy
    edit 3
        set name "VIP"
        set srcintf "wan"
        set dstintf "dmz"
        set srcaddr "all"
        set dstaddr "VIP2"
        set action accept
        set schedule "always"
        set service "ALL"
        set logtraffic all

        set nat enable

        set ippool enable

        set poolname <pool-name>

    next

 

Example 3:

Configuring Hairpin NAT when central NAT is enabled requires creating the corresponding VIP for NAT:

 

config firewall vip
    edit "VIP2"
        set extip 20.0.0.2
        set extintf "any"
        set mappedip "172.16.1.2"
    next
end

 

Port-forwarding can be added in the VIP configuration according to the setup.

 

An address object has to be created for the server in the DMZ - it will be used as a destination in the policy instead of VIP, unlike the setup without central NAT enabled:

 

config firewall address
    edit "server-VIP2"
        set subnet 172.16.1.2 255.255.255.255
    next
end

 

A firewall policy and a corresponding central NAT entry have to be configured:

 

config firewall policy
    edit 1
        set name "VIP"
        set srcintf "lan"
        set dstintf "dmz"
        set srcaddr "all"
        set dstaddr "server-VIP2"
        set action accept
        set schedule "always"
        set service "ALL"
    next
end

 

config firewall central-snat-map
    edit 1
        set srcintf "lan"
        set dstintf "dmz"
        set orig-addr "all"
        set dstaddr "server-VIP2"
    next
end

 

The NAT-ed session can be checked with the following command:

 

get system session list | grep 20.0.0.2
PROTO   EXPIRE  SOURCE         SOURCE-NAT        DESTINATION  DESTINATION-NAT
icmp    58      192.168.1.2:1  172.16.1.1:60723  20.0.0.2:8   172.16.1.2:1

 

Example 4:

If the external address of the VIP is an IP that is on the same subnet as the FortiGate but does not belong directly to the FortiGate, hairpin NAT can be achieved by specifying the WAN-side interface in the VIP's extintf.


Below is an example configuration to achieve hairpin NAT with two policies: a LAN-to-WAN policy and a WAN-to-DMZ policy:

 

diagram.png

Configurations:

 

    config firewall vip
        edit "VIP"
            set extip 20.0.0.100
            set mappedip "172.16.10.100"
            set extintf "port2"
        next
    end

    config firewall policy
        edit 1
            set name "LAN-to-WAN"
            set srcintf "port3"
            set dstintf "port2"
            set action accept
            set srcaddr "all"
            set dstaddr "all"
            set schedule "always"
            set service "ALL"
            set logtraffic all
            set nat enable
        next
        edit 2
            set name "WAN-to-DMZ"
            set srcintf "port2"
            set dstintf "port1"
            set action accept
            set srcaddr "all"
            set dstaddr "VIP"
            set schedule "always"
            set service "ALL"
            set logtraffic all
        next
    end


Flow Trace:

 

id=65308 trace_id=2220 func=print_pkt_detail line=5862 msg="vd-root:0 received a packet(proto=1, 172.16.56.200:1->20.0.0.100:2048) tun_id=0.0.0.0 from port3. type=8, code=0, id=1, seq=698."
id=65308 trace_id=2220 func=init_ip_session_common line=6047 msg="allocate a new session-003015b5"
id=65308 trace_id=2220 func=get_new_addr line=1213 msg="find DNAT: IP-172.16.10.100, port-0(fixed port)"
id=65308 trace_id=2220 func=__iprope_tree_check line=535 msg="gnum-100004, use addr/intf hash, len=2"
id=65308 trace_id=2220 func=get_new_addr line=1213 msg="find SNAT: IP-20.0.0.22(from IPPOOL), port-60418"
id=65308 trace_id=2220 func=fw_pre_route_handler line=184 msg="VIP-172.16.10.100:1, outdev-unknown"
id=65308 trace_id=2220 func=__ip_session_run_tuple line=3455 msg="DNAT 20.0.0.100:8->172.16.10.100:1"
id=65308 trace_id=2220 func=__vf_ip_route_input_rcu line=1990 msg="find a route: flag=00000000 gw-0.0.0.0 via port1"
id=65308 trace_id=2220 func=__iprope_tree_check line=524 msg="gnum-100004, use int hash, slot=51, len=2"
id=65308 trace_id=2220 func=fw_forward_handler line=990 msg="Allowed by Policy-2:"
id=65308 trace_id=2220 func=ip_session_confirm_final line=3110 msg="npu_state=0x100, hook=4"
id=65308 trace_id=2221 func=print_pkt_detail line=5862 msg="vd-root:0 received a packet(proto=1, 172.16.10.100:1->172.16.56.200:0) tun_id=0.0.0.0 from port1. type=0, code=0, id=1, seq=698."
id=65308 trace_id=2221 func=resolve_ip_tuple_fast line=5950 msg="Find an existing session, id-003015b5, reply direction"
id=65308 trace_id=2221 func=__vf_ip_route_input_rcu line=1990 msg="find a route: flag=00000000 gw-0.0.0.0 via port3"
id=65308 trace_id=2221 func=npu_handle_session44 line=1333 msg="Trying to offloading session from port1 to port3, skb.npu_flag=00000000 ses.state=00000204 ses.npu_state=0x00000100"
id=65308 trace_id=2221 func=fw_forward_dirty_handler line=439 msg="state=00000204, state2=00000001, npu_state=00000100"
id=65308 trace_id=2221 func=__ip_session_run_tuple line=3442 msg="SNAT 172.16.10.100->20.0.0.100:1"


get system session list | grep 20.0.0.100

    PROTO  EXPIRE    SOURCE            SOURCE-NAT   DESTINATION    DESTINATION-NAT
    icmp   55        172.16.56.200:1   -            20.0.0.100:8   172.16.10.100:1


Session List:

 

session info: proto=1 proto_state=00 duration=7 expire=52 timeout=0 flags=00000000 socktype=0 sockport=0 av_idx=0 use=3
origin-shaper=
reply-shaper=
per_ip_shaper=
class_id=0 ha_id=0 policy_dir=0 tunnel=/ vlan_cos=0/255
state=log may_dirty f00
statistic(bytes/packets/allow_err): org=60/1/1 reply=60/1/1 tuples=2
tx speed(Bps/kbps): 0/0 rx speed(Bps/kbps): 0/0
orgin->sink: org pre->post, reply pre->post dev=9->3/3->9 gwy=0.0.0.0/0.0.0.0
hook=pre dir=org act=dnat 172.16.56.200:1->20.0.0.100:8(172.16.10.100:1)
hook=post dir=reply act=snat 172.16.10.100:1->172.16.56.200:0(20.0.0.100:1)
misc=0 policy_id=2 pol_uuid_idx=15754 auth_info=0 chk_client_info=0 vd=0
serial=003015b5 tos=ff/ff app_list=0 app=0 url_cat=0
rpdb_link_id=00000000 ngfwid=n/a
npu_state=0x000100
no_ofld_reason: npu-flag-off


Traffic Log:

 

date=2024-08-30 time=16:24:39 eventtime=1725002679069880080 tz="+0900" logid="0000000013" type="traffic" subtype="forward" level="notice" vd="root" srcip=172.16.56.200 identifier=1 srcintf="port3" srcintfrole="undefined" dstip=20.0.0.100 dstintf="port1" dstintfrole="undefined" srccountry="Reserved" dstcountry="United Kingdom" sessionid=3151285 proto=1 action="accept" policyid=2 policytype="policy" poluuid="b062f842-5ecb-51ef-18a3-3977b8166540" policyname="WAN-to-DMZ" service="PING" trandisp="dnat" tranip=172.16.10.100 tranport=0 duration=61 sentbyte=60 rcvdbyte=60 sentpkt=1 rcvdpkt=1 appcat="unscanned"

 

It can be seen that the traffic goes through two policies but in one session. Hairpin NAT is realized, and traffic passing through the LAN-to-WAN policy is not forwarded from the WAN interface to the Internet, but instead, the process is transferred to the WAN-to-DMZ policy, where it is DestNATed and forwarded directly to the DMZ interface.

 

Hairpin NAT in Explicit web-proxy configuration :

This section outlines the configuration steps when an explicit web proxy is enabled on an interface connected to the user segment, and the destination traffic is associated with a Virtual IP (VIP) object. In this setup, the VIP's external IP address belongs to the same subnet as the interface (e.g., port1).

 

explicit webproxy - vip.drawio.png

 

Required configuration:

VIP object:

 

config firewall vip

    edit "VIP - Port1 to Port3"

        set extip 10.21.4.84 <----- The IP is in the same subnet of Port1. Not assigned to Port1.

        set mappedip "10.4.141.2"

        set extintf "port1"

        set portforward enable

        set extport 443

        set mappedport 443

    next

end

 

Proxy-policy:

 

config firewall proxy-policy

    edit 3

        set name "Hairpin NAT"

        set proxy explicit-web

        set dstintf "port3"

        set srcaddr "all"

        set dstaddr "VIP - Port1 to Port3" ----> VIP object.

        set service "webproxy"

        set action accept

        set schedule "always"

        set logtraffic all

    next

end

 

Session information:

 

session info: proto=6 proto_state=01 duration=31 expire=3570 timeout=3600 refresh_dir=both flags=00000000 socktype=0 sockport=7820 av_idx=0 use=3

origin-shaper=

reply-shaper=

per_ip_shaper=

class_id=0 ha_id=0 policy_dir=0 tunnel=/ vlan_cos=0/0

state=local may_dirty

statistic(bytes/packets/allow_err): org=3841/24/1 reply=107036/86/1 tuples=2

tx speed(Bps/kbps): 0/0 rx speed(Bps/kbps): 0/0

orgin->sink: org pre->in, reply out->post dev=6->14/14->6 gwy=0.0.0.0/0.0.0.0

hook=pre dir=org act=noop 10.4.109.2:55671->10.4.109.1:8080(0.0.0.0:0)

hook=post dir=reply act=noop 10.4.109.1:8080->10.4.109.2:55671(0.0.0.0:0)

pos/(before,after) 0/(0,0), 0/(0,0)

src_mac=00:62:6c:61:11:01

misc=0 policy_id=4294967295 pol_uuid_idx=0 auth_info=0 chk_client_info=0 vd=0

serial=0015dd12 tos=ff/ff app_list=0 app=0 url_cat=0

rpdb_link_id=00000000 ngfwid=n/a

npu_state=00000000

no_ofld_reason:  local

 

WAD session information:

falcon-esx01 # diagnose wad session list

 

Session: explicit proxy 10.4.109.2:55671(10.4.141.1:5136)->10.4.141.2:443

    id=129816 worker=0 vd=0:0 fw-policy=3

    duration=79 expire=3581 session-ttl=3600

    state=3 app=http sub_type=0 wan_opt_mode=0 dd_method=0

    SSL disabled

    to-client

        TCP Port:

            state=2 r_blocks=1 w_blocks=0 read_blocked=0

            bytes_in=2908 bytes_out=103623 shutdown=0x0

    to-server

        TCP Port:

            state=2 r_blocks=0 w_blocks=0 read_blocked=0

            bytes_in=103551 bytes_out=2703 shutdown=0x0

 

Truncated WAD debugs for reference:

 

[I]2025-07-07 07:08:02.600047 [p:19538][s:129728][r:153647] wad_dump_http_request :2915  hreq=0x7fec6dce9fa8 Received request from client: 10.4.109.2:55575

CONNECT 10.21.4.84:443 HTTP/1.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0

Proxy-Connection: keep-alive

Connection: keep-alive

Host: 10.21.4.84:443

wad_http_req_policy_set           :11332 match policy-id=3(pol_ctx:xhcf|A|7?|=d) vd=0(ses_ctx:x|Ph|Me|Hh|C|A7|O) (10.4.109.2:55575@6 -> 10.21.4.84:443@9) [VIP mapped to 10.4.141.2:443]

connect svr orig 10.4.109.2:55575->10.4.109.1:8080 out 10.4.109.2:55575->10.4.141.2:8080

wad_tcp_port_on_connect           :2068  TCP connection 0x7fec6eb5eb38 fd=124 connected 10.4.141.1:5058->10.4.141.2:443

wad_dump_fwd_http_resp            :2936  hreq=0x7fec6dce9fa8 Forward response from Internal:

HTTP/1.1 200 Connection established

Proxy-Agent: Fortinet-Proxy/1.0

 

 

Related articles:

Technical Tip: Firewall does not block incoming WAN to LAN 

Technical Note: Using an auto hairpin to browse a webpage

Technical Tip: How to disable source NAT to enable a hairpin policy or one-arm firewall

Technical Tip: Hairpin NAT (VIP) with Geo-IP Object configured in Policy Source blocks the access fr...