| In common situations, when an IPsec VPN is created from templates, internal subnets from both ends of the tunnel are selected as phase2 encrypted subnets. Those subnets are expected to traverse through the tunnel in end-to-end communication. When third parties are involved in a site-to-site, it is sometimes required to hide a real IP from being exposed, so NAT is involved. The change will require implementing NAT through the use of IPPools. When an IPPool is implemented, traffic does work in most cases and is dropped by FortiGate. See the following diagram for an example of the steps to troubleshoot and fix this issue.  Initial configurations on both devices: - FGT1 primary site configuration:
-
IPsec config: config vpn ipsec phase1-interface edit "Site1" set interface "port10" set ike-version 2 set peertype any set net-device disable set proposal aes256-sha256 set wizard-type static-fortigate set remote-gw 10.10.10.2 set psksecret ENC 0C8KlBBjW8sMXJvCsyJRUCqF3iaHncJWSvK20yOpr2NK1kSjPle5ncdBBB next end config vpn ipsec phase2-interface edit "Site1" set phase1name "Site1" set proposal aes256-sha256 set src-subnet 192.168.1.0 255.255.255.0 set dst-subnet 172.16.1.0 255.255.255.0 next end - Firewall Policy.
config firewall policy edit 1 set name "vpn_Site1_local" set srcintf "port2" set dstintf "Site1" set action accept set srcaddr "192.168.1.0" set dstaddr "172.16.1.0" set schedule "always" set service "ALL" next edit 2 set name "vpn_Site2_local” set srcintf "Site1" set dstintf "port2" set action accept set srcaddr "172.16.1.0" set dstaddr "192.168.1.0" set schedule "always" set service "ALL" next end - Static Routes (only VPN routers are shown):
config router static edit 2 set dst 172.16.1.0 255.255.255.0 set device "Site1" next edit 3 set dst 172.16.1.0 255.255.255.0 set distance 254 set blackhole enable set vrf 0 next end - FGT2 secondary site configuration:
- IPsec config:
config vpn ipsec phase1-interface edit "Site2" set interface "port10" set ike-version 2 set peertype any set net-device disable set proposal aes256-sha256 set wizard-type static-fortigate set remote-gw 10.10.10.1 set psksecret ENC uRGJQnmQ9uYV0xWyVMEiNvJTfzNdqrqCL6couXjy9Ob/cNawJ next end config vpn ipsec phase2-interface edit "Site2" set phase1name "Site2" set proposal aes256-sha256 set src-subnet 172.16.1.0 255.255.255.0 set dst-subnet 192.168.1.0 255.255.255.0 next end - Firewall Policy:
config firewall policy edit 1 set name "vpn_Site2_local" set srcintf "port2" set dstintf "Site2" set action accept set srcaddr "172.16.1.0" set dstaddr "192.168.1.0" set schedule "always" set service "ALL" next edit 2 set name "vpn_Site2_remote" set srcintf "Site2" set dstintf "port2" set action accept set srcaddr "192.168.1.0" set dstaddr "172.16.1.0" set schedule "always" set service "ALL" next end - Static Routes (only VPN routers are shown):
config router static edit 4 set dst 192.168.1.0 255.255.255.0 set device "Site2" next edit 5 set dst 192.168.1.0 255.255.255.0 set distance 254 set blackhole enable set vrf 0 next end A ping test from 192.168.1.1 to 172.16.1.1, which are both sites' internal networks, shows a normal debug flow hitting the correct route, traffic encryption, and firewall policy; while from a sniffer perspective, the packet leaves FGT1 through the tunnel interface. diagnose debug flow filter clear diagnose debug flow filter add 172.16.1.1 diagnose debug flow filter proto 1 diagnose debug flow show iprope enable diagnose debug flow show funct enable diagnose debug flow trace start 1 diagnose debug enable To stop the debugs: diagnose debug disable diagnose debug reset FGT1 # id=65308 trace_id=2 func=print_pkt_detail line=5933 msg="vd-root:0 received a packet(proto=1, 192.168.1.1:10->172.16.1.1:2048) tun_id=0.0.0.0 from local. type=8, code=0, id=10, seq=0." id=65308 trace_id=2 func=init_ip_session_common line=6133 msg="allocate a new session-00000560" id=65308 trace_id=2 func=iprope_dnat_check line=5545 msg="in-[], out-[Site1]" id=65308 trace_id=2 func=iprope_dnat_tree_check line=826 msg="len=0" id=65308 trace_id=2 func=iprope_dnat_check line=5570 msg="result: skb_flags-00000000, vid-0, ret-no-match, act-accept, flag-00000000" id=65308 trace_id=2 func=ip_session_confirm_final line=3137 msg="npu_state=0x0, hook=4" id=65308 trace_id=2 func=ipsecdev_hard_start_xmit line=662 msg="enter IPSec interface Site1, tun_id=0.0.0.0" id=65308 trace_id=2 func=_do_ipsecdev_hard_start_xmit line=222 msg="output to IPSec tunnel Site1, tun_id=10.10.10.2, vrf 0" id=65308 trace_id=2 func=esp_output4 line=917 msg="IPsec encrypt/auth" id=65308 trace_id=2 func=ipsec_output_finish line=646 msg="tunnel Site1 send to ike" FGT1 # diag sniffer packet any "host 172.16.1.1 and icmp" 4 0 l Using Original Sniffing Mode interfaces=[any] filters=[host 172.16.1.1 and icmp] 2024-10-30 10:40:47.864809 Site1 out 192.168.1.1 -> 172.16.1.1: icmp: echo request 2024-10-30 10:40:47.872813 Site1 in 172.16.1.1 -> 192.168.1.1: icmp: echo reply The problem appears when NAT is involved and IPPools are presented. Select Firewall Policy, edit the correct policy, and under 'Firewall/Network Options', select and enable NAT with option Use Dynamic IP Pool.  In the IP Pool section, select a subnet that will hide real internal IPs and present this range to the other side. For this example, 20.20.20.1-20.20.20.254 is configured.
 Firewall policy will display NAT and IPPool.
 After this change is made, traffic will stop working between the 2 sites, and the reason is msg="No matching IPsec selector, drop". diagnose debug flow filter add 192.168.1.2 172.16.1.1 and diagnose debug flow filter proto 1 diagnose debug flow show iprope en diagnose debug flow show funct en diagnose debug flow trace start 5 diagnose debug enable To stop the debugs: diagnose debug disable diagnose debug reset FGT1 # id=65308 trace_id=13 func=print_pkt_detail line=5933 msg="vd-root:0 received a packet(proto=1, 192.168.1.2:2->172.16.1.1:2048) tun_id=0.0.0.0 from port2. type=8, code=0, id=2, seq=1." id=65308 trace_id=13 func=init_ip_session_common line=6133 msg="allocate a new session-00000926" id=65308 trace_id=13 func=iprope_dnat_check line=5545 msg="in-[port2], out-[]" id=65308 trace_id=13 func=iprope_dnat_tree_check line=826 msg="len=0" id=65308 trace_id=13 func=iprope_dnat_check line=5570 msg="result: skb_flags-02000000, vid-0, ret-no-match, act-accept, flag-00000000" id=65308 trace_id=13 func=__vf_ip_route_input_rcu line=2115 msg="find a route: flag=00000000 gw-10.10.10.2 via Site1" id=65308 trace_id=13 func=__iprope_fwd_check line=820 msg="in-[port2], out-[Site1], skb_flags-02000000, vid-0, app_id: 0, url_cat_id: 0" id=65308 trace_id=13 func=__iprope_tree_check line=524 msg="gnum-100004, use int hash, slot=56, len=2" id=65308 trace_id=13 func=__iprope_check_one_policy line=2167 msg="checked gnum-100004 policy-1, ret-matched, act-accept" id=65308 trace_id=13 func=__iprope_user_identity_check line=1930 msg="ret-matched" id=65308 trace_id=13 func=__iprope_check line=2450 msg="gnum-4e20, check-ffffffffa002c9c7" id=65308 trace_id=13 func=__iprope_check_one_policy line=2167 msg="checked gnum-4e20 policy-6, ret-no-match, act-accept" id=65308 trace_id=13 func=__iprope_check_one_policy line=2167 msg="checked gnum-4e20 policy-6, ret-no-match, act-accept" id=65308 trace_id=13 func=__iprope_check_one_policy line=2167 msg="checked gnum-4e20 policy-6, ret-no-match, act-accept" id=65308 trace_id=13 func=__iprope_check line=2467 msg="gnum-4e20 check result: ret-no-match, act-accept, flag-00000000, flag2-00000000" id=65308 trace_id=13 func=get_new_addr line=1297 msg="find SNAT: IP-20.20.20.181(from IPPOOL), port-5119" id=65308 trace_id=13 func=__iprope_check_one_policy line=2420 msg="policy-1 is matched, act-accept" id=65308 trace_id=13 func=__iprope_fwd_check line=857 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-1" id=65308 trace_id=13 func=iprope_fwd_auth_check line=886 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-1" id=65308 trace_id=13 func=iprope_reverse_dnat_check line=1377 msg="in-[port2], out-[Site1], skb_flags-02000000, vid-0" id=65308 trace_id=13 func=iprope_reverse_dnat_tree_check line=918 msg="len=0" id=65308 trace_id=13 func=fw_forward_handler line=1003 msg="Allowed by Policy-1: SNAT" id=65308 trace_id=13 func=ip_session_confirm_final line=3137 msg="npu_state=0x100, hook=4" id=65308 trace_id=13 func=__ip_session_run_tuple line=3458 msg="SNAT 192.168.1.2->20.20.20.181:5119" --- IPPool will translate internal subnet 192.168.1.0 to a new subnet 20.20.20.0 which is not configured under phase2-interface on VPN Tunnel id=65308 trace_id=13 func=ipsecdev_hard_start_xmit line=662 msg="enter IPSec interface Site1, tun_id=0.0.0.0" id=65308 trace_id=13 func=_do_ipsecdev_hard_start_xmit line=222 msg="output to IPSec tunnel Site1, tun_id=10.10.10.2, vrf 0" id=65308 trace_id=13 func=ipsec_common_output4 line=923 msg="No matching IPsec selector, drop" To fix this issue VPN must be edited on both sites and include IP pools instead of real internal IPs.
Changes needed on FGT1: - Phase2 Selectors.
config vpn ipsec phase2-interface edit "Site1" set phase1name "Site1" set proposal aes256-sha256 set dhgrp 14 set src-subnet 20.20.20.0 255.255.255.0 <- Changed from 'set src-subnet 192.168.1.0 255.255.255.0'. set dst-subnet 172.16.1.0 255.255.255.0 next end Changes needed on FGT2: - Phase2 Selectors:
config vpn ipsec phase2-interface edit "Site2" set phase1name "Site2" set proposal aes256-sha256 set dhgrp 14 set src-subnet 172.16.1.0 255.255.255.0 set dst-subnet 20.20.20.0 255.255.255.0 <- Changed from 'set dst-subnet 192.168.1.0'. 255.255.255.0'. next end - Firewall policy:
config firewall policy edit 2 set name "vpn_Site2_remote" set srcintf "Site2" set dstintf "port2" set action accept set srcaddr "20.20.20.0" <- Changed from 'set srcaddr 192.168.1.0'. set dstaddr "172.16.1.0" set schedule "always" set service "ALL" next end - Static route to avoid RPF and packet drops on the destination (more info about RPF in this article: Technical Tip: Reverse Path Forwarding (RPF) implementation and use of strict-src-check enable|disable).
config router static edit 4 set dst 20.20.20.0 255.255.255.0 <- Changed from 'set dst 192.168.1.0'. set device "Site2" next edit 5 set dst 20.20.20.0 255.255.255.0 <- Changed from 'set dst 192.168.1.0'. set distance 254 set blackhole enable set vrf 0 next end After making all of the above changes, the ping test will go through, and the debug flow will show all interesting outputs. FGT1 # id=65308 trace_id=27 func=print_pkt_detail line=5933 msg="vd-root:0 received a packet(proto=1, 192.168.1.2:5->172.16.1.1:2048) tun_id=0.0.0.0 from port2. type=8, code=0, id=5, seq=1." id=65308 trace_id=27 func=init_ip_session_common line=6133 msg="allocate a new session-000013d9" id=65308 trace_id=27 func=iprope_dnat_check line=5545 msg="in-[port2], out-[]" id=65308 trace_id=27 func=iprope_dnat_tree_check line=826 msg="len=0" id=65308 trace_id=27 func=iprope_dnat_check line=5570 msg="result: skb_flags-02000000, vid-0, ret-no-match, act-accept, flag-00000000" id=65308 trace_id=27 func=__vf_ip_route_input_rcu line=2115 msg="find a route: flag=00000000 gw-10.10.10.2 via Site1" id=65308 trace_id=27 func=__iprope_fwd_check line=820 msg="in-[port2], out-[Site1], skb_flags-02000000, vid-0, app_id: 0, url_cat_id: 0" id=65308 trace_id=27 func=__iprope_tree_check line=524 msg="gnum-100004, use int hash, slot=56, len=2" id=65308 trace_id=27 func=__iprope_check_one_policy line=2167 msg="checked gnum-100004 policy-1, ret-matched, act-accept" id=65308 trace_id=27 func=__iprope_user_identity_check line=1930 msg="ret-matched" id=65308 trace_id=27 func=__iprope_check line=2450 msg="gnum-4e20, check-ffffffffa002c9c7" id=65308 trace_id=27 func=__iprope_check_one_policy line=2167 msg="checked gnum-4e20 policy-6, ret-no-match, act-accept" id=65308 trace_id=27 func=__iprope_check_one_policy line=2167 msg="checked gnum-4e20 policy-6, ret-no-match, act-accept" id=65308 trace_id=27 func=__iprope_check_one_policy line=2167 msg="checked gnum-4e20 policy-6, ret-no-match, act-accept" id=65308 trace_id=27 func=__iprope_check line=2467 msg="gnum-4e20 check result: ret-no-match, act-accept, flag-00000000, flag2-00000000" id=65308 trace_id=27 func=get_new_addr line=1297 msg="find SNAT: IP-20.20.20.181(from IPPOOL), port-5122" id=65308 trace_id=27 func=__iprope_check_one_policy line=2420 msg="policy-1 is matched, act-accept" id=65308 trace_id=27 func=__iprope_fwd_check line=857 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-1" id=65308 trace_id=27 func=iprope_fwd_auth_check line=886 msg="after iprope_captive_check(): is_captive-0, ret-matched, act-accept, idx-1" id=65308 trace_id=27 func=iprope_reverse_dnat_check line=1377 msg="in-[port2], out-[Site1], skb_flags-02000000, vid-0" id=65308 trace_id=27 func=iprope_reverse_dnat_tree_check line=918 msg="len=0" id=65308 trace_id=27 func=fw_forward_handler line=1003 msg="Allowed by Policy-1: SNAT" id=65308 trace_id=27 func=ip_session_confirm_final line=3137 msg="npu_state=0x100, hook=4" id=65308 trace_id=27 func=__ip_session_run_tuple line=3458 msg="SNAT 192.168.1.2->20.20.20.181:5122" id=65308 trace_id=27 func=ipsecdev_hard_start_xmit line=662 msg="enter IPSec interface Site1, tun_id=0.0.0.0" id=65308 trace_id=27 func=_do_ipsecdev_hard_start_xmit line=222 msg="output to IPSec tunnel Site1, tun_id=10.10.10.2, vrf 0" id=65308 trace_id=27 func=esp_output4 line=917 msg="IPsec encrypt/auth" id=65308 trace_id=27 func=ipsec_output_finish line=646 msg="tunnel Site1 send to ike" "find SNAT: IP-20.20.20.181(from IPPOOL), port-5122" <- NAT IPPool range is found. msg="policy-1 is matched, act-accept" <- Policy 1 is a match for this traffic. msg="SNAT 192.168.1.2->20.20.20.181:5122" <- NAT action has changed the IP. msg="output to IPSec tunnel Site1, tun_id=10.10.10.2, vrf 0" <- Packet output to tunnel interface. msg="IPsec encrypt/auth" <- Traffic is encrypted and sent over the tunnel. |