It is 2 AM on a Saturday. Database replication between your bare-metal rack in Frankfurt and AWS EU-Central-1 just stalled. Your alerting channel is spamming alerts, and pings are dropping 100% of packets.
A static GRE tunnel collapsed under an 800 Mbps traffic spike because someone swapped an upstream core switch without re-establishing neighbor states. When hybrid links drop, transactions back up fast. You need an automated, route-based IPsec tunnel that recalculates paths in seconds when a physical link flaps.
A production hybrid bridge between a Linux edge router and an Amazon VPC relies on two tools. strongSwan handles the IKEv2/IPsec encryption layer. FRRouting (FRR) manages dynamic BGP route exchanges. In our benchmark environment pushing 450 Mbps of sustained database traffic, this architecture maintained stable 14 ms latency and weathered AWS availability zone maintenance without dropping TCP sessions.
Quick start (5 min)
Consider this baseline setup. You manage an Ubuntu 22.04 gateway with public IP 203.0.113.10 and local subnet 192.168.10.0/24. On AWS, you provisioned a Virtual Private Gateway (VGW) attached to VPC 10.0.0.0/16. AWS supplies two tunnel endpoints. We will configure Tunnel 1 using remote public IP 198.51.100.1, inside tunnel link-local IP 169.254.10.1/30, and pre-shared key MySuperSecretKey123.
Begin by installing strongSwan and FRRouting on your Linux host:
sudo apt-get update
sudo apt-get install -y strongswan strongswan-pki frr
Adjust kernel parameters to enable forwarding and disable reverse path filtering issues:
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
sudo sysctl -w net.ipv4.conf.all.send_redirects=0
sudo sysctl -w net.ipv4.conf.default.rp_filter=2
sudo sysctl -w net.ipv4.conf.all.rp_filter=2
Store the authentication secret in /etc/ipsec.secrets:
# /etc/ipsec.secrets
203.0.113.10 198.51.100.1 : PSK "MySuperSecretKey123"
Declare the IKEv2 proposal and Virtual Tunnel Interface (VTI) mark in /etc/ipsec.conf:
# /etc/ipsec.conf
config setup
charondebug="ike 1, knl 1, cfg 0"
uniqueids=no
conn %default
keyexchange=ikev2
ike=aes256-sha256-modp2048!
esp=aes256-sha256-modp2048!
dpdaction=restart
dpddelay=10s
dpdtimeout=30s
lifetime=8h
ikelifetime=24h
type=tunnel
conn aws-tunnel-1
left=203.0.113.10
leftid=203.0.113.10
leftsubnet=0.0.0.0/0
right=198.51.100.1
rightid=198.51.100.1
rightsubnet=0.0.0.0/0
mark=42
auto=start
Instantiate the virtual tunnel interface tied to mark 42:
sudo ip tunnel add vti0 mode vti local 203.0.113.10 remote 198.51.100.1 okey 42 ikey 42
sudo ip addr add 169.254.10.2/30 remote 169.254.10.1 dev vti0
sudo ip link set vti0 up mtu 1399
Start strongSwan and verify the phase 1 and 2 handshakes:
sudo ipsec restart
sudo ipsec statusall
ping -c 3 169.254.10.1
If the ping returns with a round-trip time under 20 ms, your IPsec tunnel is passing raw packets. Next comes dynamic route propagation.
Deep dive
Engineers often run into routing black holes when connecting Linux routers to AWS. The issue almost always stems from confusing policy-based IPsec with route-based IPsec.
Route-Based vs Policy-Based Architecture
Legacy IPsec relies on a Security Policy Database (SPD). The Linux kernel inspects egress packets against exact address tuples. If a packet matches 192.168.10.0/24 -> 10.0.0.0/16, the kernel encrypts it into an ESP packet. Add a new subnet like 10.1.0.0/16 on the AWS side, and traffic silently drops. Both ends must renegotiate child security associations first.
Dynamic routing protocols make this harder. BGP exchanges hello packets over link-local addresses (169.254.0.0/16). These fall outside standard RFC 1918 encryption policies.
VTI solves this cleanly. By specifying leftsubnet=0.0.0.0/0 and rightsubnet=0.0.0.0/0 with an XFRM firewall mark (mark=42), strongSwan intercepts and encrypts every packet pushed out of interface vti0. Standard Linux routing tables now control what enters the tunnel.
Configuring FRRouting for BGP Peering
Enable the BGP daemon by editing /etc/frr/daemons:
bgpd=yes
Configure your autonomous system in /etc/frr/frr.conf. In this profile, the on-premises ASN is 65000 and the AWS VGW uses standard ASN 64512:
frr version 8.1
frr defaults traditional
hostname linux-vpn-gw
!
router bgp 65000
bgp router-id 192.168.10.1
neighbor 169.254.10.1 remote-as 64512
neighbor 169.254.10.1 description AWS-VPC-Tunnel1
neighbor 169.254.10.1 timers 10 30
!
address-family ipv4 unicast
network 192.168.10.0/24
neighbor 169.254.10.1 activate
neighbor 169.254.10.1 soft-reconfiguration inbound
exit-address-family
!
line vty
!
Apply the new routing rules:
sudo systemctl restart frr
Query the dynamic neighbor table through the FRR control shell:
sudo vtysh -c "show ip bgp summary"
sudo vtysh -c "show ip route bgp"
Look for neighbor state Established. Within 10 to 30 seconds, your kernel route table will display 10.0.0.0/16 via 169.254.10.1 dev vti0.
Advanced usage
Building Resilient Dual-Tunnel Redundancy
AWS generates two public gateway endpoints across separate Availability Zones for every VPN connection. Terminating only one endpoint leaves your link exposed during scheduled hypervisor updates. Build a second VTI interface (vti1) bound to a distinct mark (mark=43).
Use BGP AS-Path prepending so AWS prioritizes Tunnel 1 during normal operations. If Tunnel 1 drops, AWS pivots inbound traffic to Tunnel 2 within 30 seconds:
route-map TO_AWS_T1 permit 10
!
route-map TO_AWS_T2 permit 10
set as-path prepend 65000 65000
!
router bgp 65000
neighbor 169.254.10.1 remote-as 64512
neighbor 169.254.11.1 remote-as 64512
!
address-family ipv4 unicast
neighbor 169.254.10.1 activate
neighbor 169.254.10.1 route-map TO_AWS_T1 out
neighbor 169.254.11.1 activate
neighbor 169.254.11.1 route-map TO_AWS_T2 out
exit-address-family
AWS inspects your AS-Path length and steers return packets over Tunnel 1. Tunnel 2 stays in standby mode until the primary path goes dark.
Persisting VTI Interfaces Across Reboots
Ephemeral interfaces created with raw ip tunnel commands vanish after a system reboot. Save your interface attributes using systemd-networkd. Write the tunnel device declaration to /etc/systemd/network/25-vti0.netdev:
[NetDev]
Name=vti0
Kind=vti
[Tunnel]
Local=203.0.113.10
Remote=198.51.100.1
Key=42
Assign IP addressing and framing in /etc/systemd/network/25-vti0.network:
[Match]
Name=vti0
[Network]
Address=169.254.10.2/30
MTUBytes=1399
Practical tips
1. Prevent Path MTU Black Holes with MSS Clamping
When ICMP pings succeed but HTTPS or PostgreSQL handshakes hang at TLS negotiation, packet size is the cause. Encapsulation adds roughly 56 to 73 bytes of IPsec ESP overhead. A client sending standard 1500-byte frames with the Don’t Fragment (DF) bit set forces the tunnel endpoint to discard packets.
Clamp the TCP Maximum Segment Size (MSS) directly on the firewall:
sudo iptables -t mangle -A FORWARD -o vti+ -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
This forces negotiating clients to cap TCP payloads at 1359 bytes, avoiding fragmentation over 1399-byte MTU interfaces.
2. Tune DPD Timers for Rapid Recovery
By default, AWS tears down idle or unresponsive tunnels after 30 to 60 seconds. Set aggressive Dead Peer Detection (DPD) parameters in /etc/ipsec.conf:
dpdaction=restart
dpddelay=10s
dpdtimeout=30s
If line noise interrupts traffic, strongSwan detects missing heartbeats and triggers re-authentication before AWS invalidates the session.
3. The 30-Second Triage Checklist
When connectivity breaks, run these three diagnostic commands in order:
- Inspect Crypto State: Run
sudo ipsec statusall. Verify that bothIKE_SAandCHILD_SAreportESTABLISHED. If absent, checkjournalctl -u strongswanfor UDP 500/4500 drops or mismatched pre-shared keys. - Inspect Interface Counters: Run
ip -s tunnel show vti0. Confirm that TX and RX counters increment symmetrically. Large TX numbers with zero RX packets point to remote security group blocks or routing misconfigurations. - Verify BGP Peering: Run
sudo vtysh -c "show ip bgp summary". A state readingActiveorConnectindicates that FRR cannot reach169.254.10.1. Verify the local address onvti0and confirm firewall rules permit TCP port 179.
