pfSense - IPsec VPN Routed (VTI) site-to-site

I am struggling to establish a site-to-site VPN tunnel between a pfSense box and an OpenWRT/Linux. As a proof of concept, I have created this LAB to test the setup with two pfSense boxes instead. In the hopes that the understanding will help to narrow the issues with the intended setup.

The diagram below illustrates how the LAB was configured. It is a very basic setup.

We will create the tunnel and route the traffic through the tunnel.

Let's create the the tunnel in the first pfSense 192.168.45.10

HV2-LAB-PFSENSE-0 - 192.168.45.10

Navigate to the menu below.

And click in Add P1 and configure as below

Phase 1 Configuration

192.168.45.10 - HV2-LAB-PFSENSE-0

Phase 1 will use the WAN interface and we need to specify the remote gateway.

192.168.45.10 - HV2-LAB-PFSENSE-0

Change My identifier/Peer identifier to Distinguished name and set a unique name.

Use IP only if you have a static public IP address otherwise Distinguished name is recommended.

192.168.45.10 - HV2-LAB-PFSENSE-0
192.168.45.10 - HV2-LAB-PFSENSE-0
192.168.45.10 - HV2-LAB-PFSENSE-0

The Phase 1 is configured with a secure encryption algorithm.

Phase 2 Configuration

Click in Show Phase 2 Entries and Add P2 and configure as follows :

192.168.45.10 - HV2-LAB-PFSENSE-0
when the local subnet was as 10.10.10.1/30 only the remote was able to send and receive pings, after it has been changed to 10.10.10.1 pings from this host ( 10.10.10.1 ) started to get replies from the remote peer ( 10.10.10.2 ).
192.168.45.10 - HV2-LAB-PFSENSE-0
192.168.45.10 - HV2-LAB-PFSENSE-0

Save and repeat in the remote peer swapping the subnets and identifier.

Also, don't forget to swap the remote IP Address too.

192.168.45.40 - HV2-LAB-PFSENSE-1

Above is the configuration in the remote host ( 192.168.45.40 )

After configuring the remote peer ( 192.168.45.40 ) go to Status > IPsec and stablish the connection if not already connected.

Status / IPsec / Overview

Make sure that a gateway has been added for the remote peer.

192.168.45.10 - HV2-LAB-PFSENSE-0

And finally, we need to add a static route to our remote network.

The route will allow the traffic to flow through the tunnel as seen on the diagram below in green:

172.16.20.0/24 to 172.16.15.0/24
192.168.45.10 - HV2-LAB-PFSENSE-0

Don't forget to add a route in the remote peer too, otherwise the traffic won't return since the remote peer does not know how to route the traffic back.

192.168.45.40 - HV2-LAB-PFSENSE-1

We can run pings from the console to test the connectivity between the hosts.

[2.5.2-RELEASE][admin@pfSense.localdomain]/root: ping 10.10.10.2
PING 10.10.10.2 (10.10.10.2): 56 data bytes
64 bytes from 10.10.10.2: icmp_seq=0 ttl=64 time=0.736 ms
64 bytes from 10.10.10.2: icmp_seq=1 ttl=64 time=0.468 ms
64 bytes from 10.10.10.2: icmp_seq=2 ttl=64 time=0.729 ms
64 bytes from 10.10.10.2: icmp_seq=3 ttl=64 time=0.614 ms
^C
--- 10.10.10.2 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.468/0.637/0.736/0.109 ms
[2.5.2-RELEASE][admin@pfSense.localdomain]/root: ping 10.10.10.1
PING 10.10.10.1 (10.10.10.1): 56 data bytes
64 bytes from 10.10.10.1: icmp_seq=0 ttl=64 time=0.094 ms
64 bytes from 10.10.10.1: icmp_seq=1 ttl=64 time=0.132 ms
64 bytes from 10.10.10.1: icmp_seq=2 ttl=64 time=0.172 ms
^C
--- 10.10.10.1 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.094/0.133/0.172/0.032 ms
192.168.45.10 - HV2-LAB-PFSENSE-0
[2.5.2-RELEASE][admin@pfSense.localdomain]/root: ping 172.16.15.1
PING 172.16.15.1 (172.16.15.1): 56 data bytes
64 bytes from 172.16.15.1: icmp_seq=0 ttl=64 time=0.638 ms
64 bytes from 172.16.15.1: icmp_seq=1 ttl=64 time=0.497 ms
64 bytes from 172.16.15.1: icmp_seq=2 ttl=64 time=0.702 ms
64 bytes from 172.16.15.1: icmp_seq=3 ttl=64 time=0.702 ms
^C
--- 172.16.15.1 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.497/0.635/0.702/0.084 ms
192.168.45.10 - HV2-LAB-PFSENSE-0
192.168.45.10 - HV2-LAB-PFSENSE-0
192.168.45.10 - HV2-LAB-PFSENSE-0

It worth to mention that there are no traffic rules applied to the firewalls in this LAB. Don't forget that the best practices is to block all traffic and selectively allow what is needed.

Finally, we have some console output describing the connection and the strongswan configuration files.

[2.5.2-RELEASE][admin@pfSense.localdomain]/root: ipsec statusall
Status of IKE charon daemon (strongSwan 5.9.2, FreeBSD 12.2-STABLE, amd64):
  uptime: 115 minutes, since Sep 10 17:27:51 2021
  worker threads: 10 of 16 idle, 6/0/0/0 working, job queue: 0/0/0/0, scheduled: 17
  loaded plugins: charon unbound pkcs11 aes des blowfish rc2 sha2 sha1 md4 md5 random nonce x509 revocation constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 pgp dnskey sshkey ipseckey pem openssl fips-prf curve25519 xcbc cmac hmac drbg curl attr kernel-pfkey kernel-pfroute resolve socket-default stroke vici updown eap-identity eap-sim eap-md5 eap-mschapv2 eap-dynamic eap-radius eap-tls eap-ttls eap-peap xauth-generic xauth-eap xauth-pam whitelist addrblock counters
Listening IP addresses:
  192.168.45.10
  172.16.10.1
  10.10.10.1
Connections:
      bypass:  %any...127.0.0.1  IKEv1/2
      bypass:   local:  uses any authentication
      bypass:   remote: uses any authentication
        con1:  192.168.45.10...192.168.45.40  IKEv2, dpddelay=10s
        con1:   local:  [hv2-lab-pfsense-0] uses pre-shared key authentication
        con1:   remote: [hv2-lab-pfsense-1] uses pre-shared key authentication
        con1:   child:  0.0.0.0/0|/0 === 0.0.0.0/0|/0 TUNNEL, dpdaction=restart
Security Associations (1 up, 0 connecting):
        con1[8]: ESTABLISHED 114 minutes ago, 192.168.45.10[hv2-lab-pfsense-0]...192.168.45.40[hv2-lab-pfsense-1]
        con1[8]: IKEv2 SPIs: 6930b6a296b3178e_i c2549ecd735ce88b_r*, rekeying in 4 hours
        con1[8]: IKE proposal: AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048
        con1{10}:  INSTALLED, TUNNEL, reqid 1, ESP SPIs: ced7f6ea_i c5998076_o
        con1{10}:  AES_GCM_16_256/MODP_2048, 110060 bytes_i, 314972 bytes_o, rekeying in 32 minutes
        con1{10}:   0.0.0.0/0|/0 === 0.0.0.0/0|/0
[2.5.2-RELEASE][admin@pfSense.localdomain]/root:
192.168.45.10 - HV2-LAB-PFSENSE-0
# Automatically generated config file - DO NOT MODIFY. Changes will be overwritten.
starter {
        load_warning = no
}
charon {
        # number of worker threads in charon
        threads = 16
        ikesa_table_size = 32
        ikesa_table_segments = 4
        init_limit_half_open = 1000
        install_routes = no
        load_modular = yes
        ignore_acquire_ts = yes
        cisco_unity = no
        syslog {
                identifier = charon
                # log everything under daemon since it ends up in the same place regardless with our syslog.conf
                daemon {
                        ike_name = yes
                        dmn = -1
                        mgr = -1
                        ike = 4
                        chd = 4
                        job = -1
                        cfg = -1
                        knl = -1
                        net = -1
                        asn = -1
                        enc = -1
                        imc = -1
                        imv = -1
                        pts = -1
                        tls = -1
                        esp = -1
                        lib = -1
                }
                # disable logging under auth so logs aren't duplicated
                auth {
                        default = -1
                }
        }
        plugins {
                # Load defaults
                include /var/etc/ipsec/strongswan.d/charon/*.conf
                unity {
                        load = no
                }
                curve25519 {
                        load = yes
                }
                pkcs11 {
                        load = yes
                        modules {
                                opensc {
                                        load_certs = yes
                                        path = /usr/local/lib/opensc-pkcs11.so
                                }
                        }
                }
        }
}
/var/etc/ipsec/strongswan.conf - 192.168.45.10 - HV2-LAB-PFSENSE-0
# This file is automatically generated. Do not edit
connections {
        bypass {
                remote_addrs = 127.0.0.1
        }
        con1 {
                fragmentation = yes
                unique = replace
                version = 2
                proposals = aes256-sha256-modp2048
                dpd_delay = 10s
                dpd_timeout = 60s
                rekey_time = 25920s
                reauth_time = 0s
                over_time = 2880s
                rand_time = 2880s
                encap = no
                mobike = no
                local_addrs = 192.168.45.10
                remote_addrs = 192.168.45.40
                local {
                        id = fqdn:hv2-lab-pfsense-0
                        auth = psk
                }
                remote {
                        id = fqdn:hv2-lab-pfsense-1
                        auth = psk
                }
                children {
                        con1 {
                                close_action = start
                                dpd_action = restart
                                policies = no
                                life_time = 3600s
                                rekey_time = 3240s
                                rand_time = 360s
                                start_action = start
                                remote_ts = 10.10.10.2,0.0.0.0/0
                                local_ts = 10.10.10.1,0.0.0.0/0
                                reqid = 1
                                esp_proposals = aes256gcm128-modp2048
                        }
                }
        }
}
secrets {
        ike-0 {
                secret = 0sZTU1OWM3NTI0NzgxODhmM2ZjYTA3YWIzZTVmY2QxZmYwMmY1YzU1NTc0YjU3NjkyMGM2N2M0NDM=
                id-0 = %any
                id-1 = fqdn:hv2-lab-pfsense-1
        }
}
/var/etc/ipsec/swanctl.conf - 192.168.45.10 - HV2-LAB-PFSENSE-0

Resources

[strongSwan] IPSec route based VPN - VTI interface TX Errors NoRoute
There are extra information on this email thread.
strongSwan / IPsec
strongSwan / IPsec
pfSense Configuration Recipes — Routing Internet Traffic Through a Site-to-Site IPsec Tunnel | pfSense Documentation
swanctl.conf - strongSwan
Redmine
Configuration Files - strongSwan
Redmine
Test ikev2/net2net-psk
How to Create a Virtual Interface for IPSEC Tunnel
I’ve created an IPSEC connection to a VPN provider with StrongSwan on OpenWRT. Everything is working fine as expected and I’m able to use iptables to route a few machines out through the VPN tunnel. However, I would like to use OpenWRT web interface to do the routing but can’t do it. The closest …
Tutorial for policy based ipsec tunnel
IPsec Site-to-Site
IPsec Site-to-Site This article assumes you have enabled IPSec on your OpenWrt router as described in the basics guide and the firewall guide. Now we want to build the first site to site tunnel. Topology The task to achive is the connectivity of our home (W)LAN with our company’s networks. To mak…
[Solved] How to create VTI interface for IPsec Site-To-Site in OpenWrt?
I have successfully established IPsec Site-To-Site VPN. Now I am unable to create a VTI interface for the same. Here is my ipsec statusall output : Status of IKE charon daemon (strongSwan 5.8.2, Linux 4.14.171, x86_64): uptime: 13 minutes, since Jun 28 11:03:35 2020 worker threads: 10 of 16 id…
Working example from openwrt forums
Virtual Private Networks — IPsec — Routed IPsec (VTI) | pfSense Documentation
Routed IPsec on pfSense 2.4.4
For enterprises with network strategies built on pfSense Plus, Netgate engineers and consultants are the premier experts on networking software & hardware
Route-based VPNs - strongSwan
Redmine
updown Plugin - strongSwan
Redmine
/src/_updown/_updown.in - strongSwan
Redmine
OpenWRT: Microsoft Azure to Cloudera CDH via VPN Gateway
In this post, we’ll show you how to create and connect your local home network to the Azure space network. We’ll take this a step further by connecting this Microsoft Azure VM insta…
Taking traffic dumps on Linux - strongSwan
Redmine
strongSwan - ArchWiki
IPsec on Linux – Strongswan Configuration w/Cisco IOSv (IKEv2, Route-Based VTI, PSK) | Question Computer

https://cloudnetworking.io/2018/01/03/azure-routed-vpn-with-strongswan-on-linux/

Configuring an IPSec connection with VTI
The OpenStack VPN-as-a-Service (VPNaaS) is enabled in IBM Cloud Manager with OpenStack. VPNaaS is optional. For more information about VPNaaS, see the OpenStack community documentation.
IPsec on Linux – Strongswan Configuration w/Cisco IOSv (IKEv2, Route-Based VTI, PSK) | Question Computer
iptables traffic flow
[strongSwan-dev] route a packet to a VTI device, it gets NoRoute error
Issue #2564: Bypass LAN plugin breaks route-based VPN in default configuration - strongSwan
Redmine
[strongSwan] IPSec route based VPN - VTI interface TX Errors NoRoute