IPsec Basics

For an overview over all existing Virtual private network (VPN)-related articles in the OpenWrt wiki, please visit vpn.overview

:!: This page is about strongSwan. The old racoon documentation can be found here.

A quick starters guide based on OpenWrt Attitude Adjustment 12.09. Maybe it will save you and me time if one has to setup an IPsec VPN in the future. Hopefully it will encourage other people to use Openwrt as an IPsec VPN router. We cannot provide a graphical user interface at the moment but at least it is a solid alternative to commercial IPsec appliances. If you came here for information about Openswan on OpenWrt you may be disappointed. This guide is only about strongSwan.

Packages

If not already installed on your router you need the at least those packages. Ensure that you use strongSwan 5.0.0 or higher. Older versions will not work due to differences in configuration handling.

  • strongswan-full 5.0.0: everything needed for IPsec tunnels
  • ip: Required to make scripting easier
  • iptables-mod-nat-extra: For VPN networks with overlapping IP addresses
  • djbdns-utils: for simpler name resolving than old "nslookup | awk" thing (may also be named djbdns-tools)

Altogether those packages will eat up about 4 MB of your router's flash memory. Maybe it is time for an extroot installation?

Configuration concept

Starting with strongSwan 5.0 the one and only IPsec daemon is Charon. The former Pluto daemon is no longer available. For this, one normally edits some files

  • /etc/strongswan.conf: Central configuration file
  • /etc/ipsec.conf: Tunnel definitions
  • /etc/ipsec.secrets: List of preshared keys
  • /etc/ipsec.d: Folder for certificates

The major challenge is handling ipsec.conf with clean integration into the OpenWrt configuration concept. To solve this we will use a hierarchical configuration process. That involves

  • /etc/strongswan.conf: The central strongSwan configuration file.
  • /etc/config/ipsec: The OpenWrt UCI compatible IPsec configuration file.
  • /etc/init.d/ipsec: The strongSwan start script. It will generate the required configuration files.
  • /var/ipsec/ipsec.conf: The generated IPsec config file, automatically symlinked into /etc.
  • /var/ipsec/ipsec.secrets : The generated IPsec preshared keys file, automatically symlinked into /etc.

Here a short example of the configuration methodology when having two VPN tunnels to ACME and Yabadoo networks

#/etc/config/ipsec
config 'remote' 'ACME'
  option 'enabled' '1'
  option 'gateway' '1.2.3.4'
  list   'tunnel' 'acme_lan'
  ...

config 'tunnel' 'acme_lan'
  option 'local_subnet' '192.168.213.64/26'
  option 'remote_subnet' '192.168.10.0/24'
  ...

config 'remote' 'Yabadoo'
  option 'enabled' '1'
  option 'gateway' '5.6.7.8'

Read more about the complete syntax for /etc/config/ipsec.

IKE Daemon

To let Charon run as a background daemon we can place a hook in the init environment. Therefore create the file /etc/init.d/ipsec and set the executable bit. This script will read configuration from /etc/config/ipsec and then generate the actual IPsec system configuration files (/var/ipsec/ipsec.conf and /var/ipsec/ipsec.secrets) before starting IPsec. Remark: This script is in an early alpha state. It currently works for site to site tunnels with preshared keys. Feel free to enhance it.

#!/bin/sh /etc/rc.common
#/etc/init.d/ipsec - version 4
 
NAME=ipsec
START=60
STOP=60
 
. /lib/functions.sh
 
FileSecrets=/var/ipsec/ipsec.secrets
FileConn=/var/ipsec/ipsec.conf
 
FolderCerts=/var/ipsec/ipsec.d
 
ConfigUser()
{
  local enabled
  local xauth
  local name
  local password
  local crt_subject
 
  config_get_bool enabled $1 enabled 0
  [[ "$enabled" == "0" ]] && return
 
  config_get_bool xauth       $1 xauth       0
  config_get      name        $1 name        ""
  config_get      password    $1 password    ""
 
  if [ $xauth -eq 1 -a "$name" != "" -a "$password" != "" ]; then
    echo "$name : XAUTH \"$password\"" >> $FileSecrets
  fi
}
 
 
ConfigPhase1() {
  local encryption_algorithm
  local hash_algorithm
  local dh_group
 
  config_get encryption_algorithm  "$1" encryption_algorithm
  config_get hash_algorithm        "$1" hash_algorithm
  config_get dh_group              "$1" dh_group
 
  Phase1Proposal=${Phase1Proposal}","${encryption_algorithm}-${hash_algorithm}-${dh_group}
}
 
ConfigTunnel() {
  local local_subnet
  local local_nat
  local remote_subnet
  local p2_proposal
  local pfs_group
  local encryption_algorithm
  local authentication_algorithm
 
  config_get local_subnet             "$1"           local_subnet
  config_get local_nat                "$1"           local_nat ""
  config_get remote_subnet            "$1"           remote_subnet
  config_get p2_proposal              "$1"           p2_proposal
  config_get pfs_group                "$p2_proposal" pfs_group
  config_get encryption_algorithm     "$p2_proposal" encryption_algorithm
  config_get authentication_algorithm "$p2_proposal" authentication_algorithm
 
  [[ "$local_nat" != "" ]] && local_subnet=$local_nat
 
  p2_proposal="${encryption_algorithm}-${authentication_algorithm}-${pfs_group}"
 
  echo "conn $ConfigName-$1" >> $FileConn
  echo "  keyexchange=ikev1" >> $FileConn
  echo "  left=$LocalGateway" >> $FileConn
  echo "  right=$RemoteGateway" >> $FileConn
  echo "  leftsubnet=$local_subnet" >> $FileConn
  if [ "$AuthenticationMethod" = "psk" ]; then
    echo "  authby=psk" >> $FileConn
    echo "  rightsubnet=$remote_subnet" >> $FileConn
# should be auto=route when going to 5.0.1
    echo "  auto=start" >> $FileConn
  elif [ "$AuthenticationMethod" = "xauth_psk_server" ]; then
    echo "  authby=xauthpsk" >> $FileConn
    echo "  xauth=server" >> $FileConn
    echo "  modeconfig=pull" >> $FileConn
    echo "  rightsourceip=$remote_subnet" >> $FileConn
    echo "  auto=add" >> $FileConn
  fi
  if [ "$LocalIdentifier" != "" ]; then
    echo "  leftid=$LocalIdentifier" >> $FileConn
  fi
  if [ "$RemoteIdentifier" != "" ]; then
    echo "  rightid=$RemoteIdentifier" >> $FileConn
  fi
 
#  echo "  auth=esp" >> $FileConn
  echo "  esp=$p2_proposal" >> $FileConn
  echo "  ike=$Phase1Proposal" >> $FileConn
  echo "  type=tunnel" >> $FileConn
}
 
ConfigRemote() {
  local enabled
  local gateway
  local pre_shared_key
  local authentication_method
  local local_identifier
  local remote_identifier
 
  ConfigName=$1
 
  config_get_bool enabled "$1" enabled 0
  [[ "$enabled" == "0" ]] && return
 
  config_get gateway               "$1" gateway
  config_get pre_shared_key        "$1" pre_shared_key
  config_get authentication_method "$1" authentication_method
  config_get local_identifier      "$1" local_identifier
  config_get remote_identifier     "$1" remote_identifier
 
  AuthenticationMethod=$authentication_method
  LocalIdentifier=$local_identifier
  RemoteIdentifier=$remote_identifier
 
  RemoteGateway=$gateway
  if [ "$RemoteGateway" = "any" ]; then
    RemoteGateway="%any"
    LocalGateway=`ip route get 1.1.1.1 | awk -F"src" '/src/{gsub(/ /,"");print $2}'`
  else
    LocalGateway=`ip route get $RemoteGateway | awk -F"src" '/src/{gsub(/ /,"");print $2}'`
  fi
  echo "$LocalGateway $RemoteGateway : PSK \"$pre_shared_key\"" >> $FileSecrets
 
  Phase1Proposal=""
  config_list_foreach "$1" p1_proposal ConfigPhase1
  Phase1Proposal=`echo $Phase1Proposal | cut -b 2-`
 
  config_list_foreach "$1" tunnel ConfigTunnel
}
 
PrepareEnvironment() {
  for d in cacerts aacerts ocspcerts crls acerts; do
    mkdir -p $FolderCerts/$d 2>/dev/null
  done
 
  if [ ! -L /etc/ipsec.d ]; then
    rm -rf /etc/ipsec.d 2>/dev/null
    ln -s $FolderCerts /etc/ipsec.d
  fi
 
  if [ ! -L /etc/ipsec.secrets ]; then
    rm /etc/ipsec.secrets 2>/dev/null
    ln -s $FileSecrets /etc/ipsec.secrets
  fi
 
  if [ ! -L /etc/ipsec.conf ]; then
    rm /etc/ipsec.conf 2>/dev/null
    ln -s $FileConn /etc/ipsec.conf
  fi
 
  echo "# generated by /etc/init.d/ipsec" > $FileConn
  echo "version 2" > $FileConn
  echo "config setup" >> $FileConn
  echo "  charondebug = \"ike 2,knl 2\"" >> $FileConn
 
  echo "# generated by /etc/init.d/ipsec" > $FileSecrets
}
 
CheckInstallation() {
  if [ ! -x /usr/sbin/ip ]; then
    echo /usr/sbin/ip missing
    echo install with \"opkg install ip\"
    exit
  fi
 
  for f in aes authenc cbc hmac md5 sha1; do
    if [ `opkg list kmod-crypto-$f | wc -l` -eq 0 ]; then
      echo kmod-crypto-$f missing
      echo install with  \"opkg install kmod-crypto-$f --nodeps\"
      exit
    fi
  done
 
  for f in aes gmp hmac kernel-netlink md5 random sha1 updown attr resolve; do
    if [ ! -f /usr/lib/ipsec/plugins/libstrongswan-${f}.so ]; then
      echo /usr/lib/ipsec/plugins/$f missing
      echo install with \"opkg install strongswan-mod-$f --nodeps\"
      exit
    fi
  done
}
 
start() {
  CheckInstallation
  PrepareEnvironment
 
  config_load users
  config_foreach ConfigUser user
 
  config_load ipsec
  config_foreach ConfigRemote remote
 
  /usr/sbin/ipsec start
}
 
stop() {
  /usr/sbin/ipsec stop
}

Before you start Charon with the web interface you should make a dry run from command line. This will show you if there are any errors in your generated configuration file /etc/ipsec.conf. Afterwards you can control startup behaviour with LuCI.

Hardware performance

In the times of broadband internet connections encryption and decryption speed of SOME low-end routers can limit throughput of VPN tunnels. CPU utilization can max out at 100 percent and impacts other services of the device like a web server. FOR REFERENCE: strongSwan will run just FINE on a WNDR3700 (MIPS 680 Mhz, 64 Mb RAM). If your router is underpowered, here are some other options:

  • Older firewall devices with hardware accelerated VPN are sold for a few bucks on Ebay. Juniper Netscreen 5GT for example can easily reach a VPN throughput of 20 MBit/sec. Downside is that firmware updates are only possible with a Juniper support contract. So check twice for a bargain.
  • Firewall devices are build to support IPsec out of the box. A convenient web interface helps the administrator to build a tunnel in a few seconds. OpenWrt still lacks a standard LuCI config panel. If you only go with 1-5 VPN tunnels this should be no concern to you.

To find the right OpenWrt hardware for your VPN you should have a look at the following benchmark table. It is build on a simple test without any claim of perfection. Nevertheless the numbers are quite close to what you can expect from an AES 128/256 bit encrypted IPsec Tunnel connection with standard kernel modules. You may notice that those numbers differ from what is written on the OpenSSL wiki page. But simply remember: The tests over there do not include network traffic. If you want to add a new device onto the list check the encryption throughput using the following prerequisites

  • Logon to a fast Linux machine
  • Use a direct LAN connection to the router
  • Ensure the router is idle
  • Transfer 100 MB of data using ssh
  • Calculate the speed from the elapsed time. Throughput = 800 / SecondsElapsed

ssh -2 -c aes128-cbc root@<router> time dd if=/dev/zero bs=500000 count=200 > /dev/null
ssh -2 -c aes256-cbc root@<router> time dd if=/dev/zero bs=500000 count=200 > /dev/null

You can have a look at the realtime traffic graph in a dry run afterwards to verify the speed. But do not open it during your test because it invalidates the results.

CPU MHz tested device AES128 (s) AES128 (MBit/s) AES256 (s) AES256 (MBit/s)
MIPS 24k 680 D-Link DIR-825 Netgear WNDR3700 28.2 28.5 32.4 24.6
MIPS 24k 400 TP-Link TL-WR703N 47.7 16.5 56.1 14.2
MIPS R3000 125 Asus WL-500g 164.8 4.8 183.5 4.3

IPsec Tuning

If you use a default OpenWrt installation you will discover that using the SHA1 hashing function will hit VPN performance. If you go for raw throughput MD5 can be a helpful alternative. One may remark that MD5 is not very secure but for IPsec connections it should be enough as we are talking about hash values of encrypted data with a key that is changed every hour according to phase 2 proposals. A good tradeoff could be to choose AES256/SHA1 for phase 1 and AES128/MD5 for phase 2.

Read on if you have some time and want to enhance your VPN speed. The kernel IPsec architecture relies on different crypto providers. E.g. if you build a tunnel with SHA1 checksums you must have a module that can calculate those values. A look at /proc/crypto will reveal what modules are loaded and which algorithms they provide. The standard Linux Kernel modules are far from being optimized. At least with kernel 3.2 someone has taken care of SHA1. Those of you that are on MIPS big endian machines can replace the default aes_generic.ko, sha_generic.ko, cbc.ko and md5.ko modules with a single assembler optimized mcespi.ko. Besides of being faster it has some nice characteristics:

  • SHA1 calculation works on registers only
  • A lot of memcpy operations have been removed from MD5 and AES.
  • AES memory footprint is lowered from 16K to 8,2K
  • Avoid little endian byte swapping in AES
  • 21K module size in contrast to 4 modules with 45K

If you are on AR7161 you should ensure that you already have unaligned access patch 1 from trunk and the not yet implemented unaligned access patch 2. It will free CPU from handling unaligned access exceptions so that you can reach these results:

OpenWrt Device AES256/SHA1 (MBit/s) AES128/SHA1 (MBit/s) AES128/MD5 (MBit/s)
trunk + mcespi.ko MIPS 24K @ 680 MHz (AR7161) 37.5 42.4 47.6
10.03.1-rc6 MIPS 24K @ 680 MHz (AR7161) 18.0 19.6 30.3

The module is in early alpha development an the easiest way to install it includes a few steps.

  • create a buildroot environment
  • compile an image for your router once
  • put the mcespi.c into the the folder build_dir/linux-<arch>/linux-<X.Y.Z>/crypto
  • Include the line obj-$(CONFIG_CRYPTO_MD5) += mcespi.o into build_dir/linux-<arch>/linux-<X.Y.Z>/crypto/Makefile
  • compile the image once again.
  • Afterwards you will find build_dir/linux-<arch>/linux-<X.Y.Z>/crypto/mcespi.ko
  • Put mcespi.ko to your router into /lib/modules/<X.Y.Z>
  • Load the module with insmod
  • For automatic loading create a new /etc/modules.d/09-crypto-mcespi with corresponding content.

If you are not on MIPS big endian but you have at least kernel 2.6.39 you can head for SHA1 performance optimization. Download sha_optimized.c and build it as a module. FIXME Ticket #10637 tries to implement that patch into trunk. At least on Atheros AR7161 platform and current trunk this leads to an oops. If you can help and find out why just put your feedback here or over there. Note: On current trunk the default kernel is 3.3 and sha_optimized.c does not have to be backported anymore.

mv 259-crypto_SHA1_3.2_backport.patch target/linux/generic/patches-XXX
make target/linux/clean
make world

What's next

After the basic setup you should continue with the firewall modifications.

Current Issues

The latest trunk includes strongSwan 5.1.1-1. It will compile if you remove curl, but will not run due to a module loading issue. One workaround is to configure the charon.load string in strongswan.conf, which explicitly loads the modules you want/need.

Example:

Charon {
   load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default updown attr farp dhcp
....

Tag

Back to top

doc/howto/vpn.ipsec.basics.txt · Last modified: 2014/11/09 05:09 by ahimoth