Understanding the Minimal XFRM Policy Requirement in IPsec VPN Tunnel Configurations


2 views

In your StrongSwan-RouterOS IPsec setup, the three XFRM policies you're observing represent the fundamental security associations required for bidirectional tunneled communication. Let's dissect each component:

# Policy 1: Forward Path (site B → site A)
src 10.50.0.0/16 dst 10.10.0.0/16 dir fwd
# Policy 2: Inbound Path (site B → site A)
src 10.50.0.0/16 dst 10.10.0.0/16 dir in  
# Policy 3: Outbound Path (site A → site B)
src 10.10.0.0/16 dst 10.50.0.0/16 dir out

The apparent "missing" forward policy for traffic from 10.10.0.0/16 to 10.50.0.0/16 isn't actually required because:

  1. Outbound traffic from site A initiates through the dir out policy
  2. The return path is handled by the dir in policy on site A
  3. Intermediate forwarding is managed by the existing dir fwd policy

Consider this packet journey when 10.10.0.89 pings 10.50.4.11:

1. Outbound phase:
   [10.10.0.89] → (out policy match) → [IPsec encap] → [Public Internet]

2. Return phase:
   [10.50.4.11] → (fwd policy match) → [IPsec decap] → [10.10.0.89]

Here's how you could explicitly define policies using ip xfrm commands:

# Outbound policy
ip xfrm policy add src 10.10.0.0/16 dst 10.50.0.0/16 \\
  dir out tmpl src PUBLIC_IP_A dst PUBLIC_IP_B \\
  proto esp mode tunnel

# Inbound policy  
ip xfrm policy add src 10.50.0.0/16 dst 10.10.0.0/16 \\
  dir in tmpl src PUBLIC_IP_B dst PUBLIC_IP_A \\
  proto esp mode tunnel

# Forward policy
ip xfrm policy add src 10.50.0.0/16 dst 10.10.0.0/16 \\
  dir fwd tmpl src PUBLIC_IP_B dst PUBLIC_IP_A \\
  proto esp mode tunnel

The architecture works because:

  • Linux networking stack handles local delivery via dir in
  • Forwarded traffic only needs one direction defined (opposite to initiator)
  • Route-based VPNs often require fewer policies than policy-based VPNs

To verify policy effectiveness, use:

ip xfrm policy count
ip xfrm monitor policy

When examining a functional site-to-site IPsec tunnel between StrongSwan (v5.2.0) and RouterOS, we observe an interesting policy configuration with only three XFRM policies instead of the expected four. Let's break down what each policy does:

# Policy 1: Forward direction (B→A)
src 10.50.0.0/16 dst 10.10.0.0/16
    dir fwd priority 2947 ptype main
    tmpl src PUBLIC_IP_B dst PUBLIC_IP_A
        proto esp reqid 1 mode tunnel

# Policy 2: Input direction (B→A)
src 10.50.0.0/16 dst 10.10.0.0/16
    dir in priority 2947 ptype main
    tmpl src PUBLIC_IP_B dst PUBLIC_IP_A
        proto esp reqid 1 mode tunnel

# Policy 3: Output direction (A→B)
src 10.10.0.0/16 dst 10.50.0.0/16
    dir out priority 2947 ptype main
    tmpl src PUBLIC_IP_A dst PUBLIC_IP_B
        proto esp reqid 1 mode tunnel

The apparent missing forward policy from A→B isn't actually necessary due to how Linux routing works. When traffic originates from 10.10.0.0/16 destined for 10.50.0.0/16:

  1. The output policy matches and encrypts the traffic
  2. On the return path, the input policy handles decryption
  3. The forward policy only applies to transit traffic

The ping route trace reveals why:

ping -R 10.50.4.11
PING 10.50.4.11 (10.50.4.11): 56 data bytes
64 bytes from 10.50.4.11: icmp_seq=0 ttl=62 time=10.872 ms
RR:     10.10.0.89
    10.50.0.1
    10.50.4.11
    10.50.4.11
    10.50.4.11
    10.10.0.2
    10.10.0.89

This shows:

  • Outbound traffic goes directly through the tunnel (no forward policy needed)
  • Return traffic hits the forward policy (B→A)
  • Local traffic uses input/output policies only

To verify your tunnel configuration, use these diagnostic commands:

# Check installed policies
ip xfrm policy

# View security associations
ip xfrm state

# Check routing table
ip route show table all

The complete policy set would look like this in a configuration:

# SPD configuration for StrongSwan
conn site-to-site
    left=10.10.0.0/16
    right=10.50.0.0/16
    leftsubnet=10.10.0.0/16
    rightsubnet=10.50.0.0/16
    authby=secret
    ike=aes256-sha1-modp1024!
    esp=aes256-sha1!
    keyingtries=0
    ikelifetime=8h
    lifetime=1h
    type=tunnel
    auto=start