You are here

IPsec based VPN using FreeBSD

Ivan Radovanovic's picture

Since I wasn't really able to find information how to set this up on one place here is short recipe.


  • Kernel compiled with
    option IPSEC
    device crypto
    option IPSEC_NAT_T
  • installed /usr/ports/security/ipsec-tools

After this some introduction to entire problem comes handy. Most important thing to understand here is how IPsec actually works within operating system - you can get some help here from setkey (8) with important thing to note: there are 2 settings which are important here:
- How to encapsulate traffic and what traffic should be encrypted, being defined by SPD entries in kernel
- What algorithms and keys are used for actual encryption, being defined by SAD entries

So what you do if you want to setup simple encrypted tunnel between two systems with pre-agreed password - you simply setup gif (4) tunnel between them, you define SPD to say that all traffic between internal address of system 1 and internal address of system 2 is actually to be tunneled using esp between external address of system 1 and external address of system 2 and you define keys and encryption algorithms in SAD.

However setting proper VPN is little bit more tricky - obviously we want to have some way to authenticate ourselves, but we don't want to share encryption keys prior to communication, but instead to be able to agree on them on the fly - this is where racoon (8) comes into play - it is racoon that is able to authenticate peers and to negotiate keys and algorithms for encryption and to update kernel SAD with correct values on the fly. Unfortunately it seems that configuring and debugging racoon is real pain unless you had previous experience doing that.

Here is little example how to configure racoon to talk to Cisco - first of all there is possible to have several different basic approaches here:
- we can have permanent vpn connection (from OS and application's point of view) - whenever connection is needed it will be established if it is not already there
- or we can have connection that is established and broken on demand
I will give more details on first approach but I will also provide hints how to achieve second if desired.

  1. Collect all parameters you might need
    This would include
    • Cisco public address - we will call it C.C.C.C (all capital)
    • FreeBSD public address - we will use F.F.F.F here (again all capital). Please note if Cisco is being server this public address might be internal address in your network as well
    • Cisco private address - we will use c.c.c.c (locase)
    • FreeBSD private address - we will use f.f.f.f. Please note if Cisco is being server this private address should be address Cisco is attempting to give to its clients - usually
    • Cisco network we are connection to - we will use cnet
    • FreeBSD private network - we will use fnet. Please note if Cisco is being used as server this network should be the one Cisco is attempting to give to its clients - usually
  2. Configure tunnel between two systems
    We can create simple script to do that:
    /sbin/ifconfig gif0 destroy
    /sbin/ifconfig gif0 create
    /sbin/ifconfig gif0 tunnel F.F.F.F C.C.C.C
    /sbin/ifconfig gif0 inet f.f.f.f c.c.c.c
    /sbin/route add cnet -iface gif0
    if gif0 is used for something else on your system you can use gif1 or gif12 for this, make sure that you replace all addresses with appropriate for your setup
    You can execute this file immediately.
  3. Configure security policies
    Configuring security policies is writing simple script to be loaded using setkey -f
    spdadd fnet cnet any -P out ipsec esp/tunnel/F.F.F.F-C.C.C.C/require;
    spdadd cnet fnet any -P in ipsec esp/tunnel/C.C.C.C-F.F.F.F/require;
    replace all addresses (fnet, cnet, F.F.F.F, C.C.C.C) with your actual addresses.
    You can load this file immediately.
  4. Configure racoon
    You can for example store configuration in racoon's default config file (/usr/local/etc/racoon/racoon.conf)
    path pre_shared_key "/usr/local/etc/racoon/psk.txt";
    log warning;
    padding {
    maximum_length 20; # maximum padding length.
    randomize off; # enable randomize length.
    strict_check off; # enable strict check.
    exclusive_tail on; # extract last one octet.
    remote anonymous {
    exchange_mode aggressive;
    situation identity_only;
    nonce_size 16;
    lifetime time 23 hour;
    support_proxy on;
    proposal_check claim; # obey, strict or claim
    dpd_delay 20;
    nat_traversal force;
    my_identifier keyid tag "XXXX"; # Cisco connection name, or group name
    xauth_login "YYYY"; # Xauth username goes here
    mode_cfg on;
    # script "/usr/local/etc/racoon/" phase1_up;
    # script "/usr/local/etc/racoon/" phase1_down;
    ike_frag on;
    esp_frag 552;
    proposal {
    encryption_algorithm aes;
    hash_algorithm sha1;
    authentication_method xauth_psk_client; # make sure this matches your authentication type
    lifetime time 3600 sec;
    dh_group 2;
    sainfo anonymous {
    # pfs_group 2;
    lifetime time 3600 sec;
    encryption_algorithm aes;
    authentication_algorithm hmac_sha1;
    compression_algorithm deflate;

    And we also need to create file with preshared keys (psk.txt in example above)
    C.C.C.C conn_password
    XXXX conn_password
    YYYY xauth_password
    as you can see entries in this file can be both IP addresses and usernames used in racoon.conf. This file should not be readable by group or world otherwise racoon will complain about that and ignore file.
    After creating this two files you can start racoon using racoon -F -f config_file - if you saved racoon configuration as /usr/local/etc/racoon/racoon.conf you can omit -f switch completely.
  5. Make connection connect
    Do something which should generate traffic through VPN (for example ping one of machines behind Cisco from external BSD machine) - that should trigger VPN connection establishing and if everything is correct you will be able to ping.
  6. What to do if things don't work
    Some ideas
    • Increase log level for racoon (log debug in config file for example)
    • You will probably receive error whether phase 1 or phase 2 is failing - phase 1 is the one with settings in remote and remote/proposal sections, phase 2 in the one with settings in sainfo
    • Important parameters to double check (besides obvious authentication parameters) are those encryption related - algorithm, authentication hash and Diffie-Hellman group (which is called pfs_group for phase 2 (PerFect Security group)

Some further discussion
Instead of having anonymous sainfo and remote you can give them name (which should be equal to IP address of machine you are connecting to) - that way you can have settings for multiple VPN connections in one file. It is also good idea to have lines
ike_frag on;
esp_frag 552;
in your config file if you are behind home adsl modem/router - this should allow IPsec to flow through it (operating system will try to keep esp fragments under 552 bytes, so they probably won't be split by router which would break them).

Some pointers on configuring vpn which requires manual startup/shutdown
Will follow shortly :-)


Ivan Radovanovic's picture

It seems sometimes racoon is still enforcing ipsec_nat_t even if it is not needed by setup (ie if there are no NAT devices between FreeBSD and other server). That will result in inability to establish vpn connection because racoon would keep sending packets from port 4500 to port 500 on other end. Solution for this is to force racoon to use port 500 on its end - to do that:

  • make sure you have nat_traversal off in remote section
  • make sure you explicitly specify listen section without isakmp_natt parameter there - for example:

    listen {