pipsecd with FreeBSD

So, I've been examining a number of tunneling protocols, and for my needs I have decided that I like IPsec's ESP, defined in RFC 2406. It is simple (unlike such things as PPTP and L2TP) and provides reasonable encryption. ESP can operate in two different modes: transport mode, which provides encryption but no IP header encapsulation, and tunnel mode, which is a full IP tunnel. Tunnel mode is what I will be using here.

For those of you who aren't aware, "VPN" is a vague term invented by marketroids that normally means a network tunnel. If you think you want to set up a VPN, you probably want to set up a tunnel. Do yourself a favor and stop using the term "VPN". The salespeople and managers you work with will complain, but you will gain some respect from the people who matter.

FreeBSD comes with a generic tun(4) virtual network interface, which connects to userland processes to perform the specifics of a tunneling protocol. IPsec ESP is implemented by a program called pipsecd. This can be found in the ports collection in net/pipsecd.

The reason I am putting together this document is because:

  1. pipsecd is mostly undocumented
  2. After having played with it, I need to resolve some issues so I can put this into production
  3. I see room for improvement in terms of usability that might be able to contribute to pipsecd and FreeBSD

Configuring pipsecd

"sa" apparently stands for Security Association, and I don't know if that is a standard IPsec term, or if it just goes with this program. I haven't figured out what "spi" stands for, but it seems to be an identifier to associate key information with a direction on the tunnel.

I think (because it isn't documented and I haven't looked at the source that closely) that you need to create two SA's for each tunnel. Each SA includes an SPI (to be referenced by the interface), an indication of the encryption algorithm to be used, and a key. The outgoing SA must include the destination IP address to be used in the encapsulating IP header. The key of the outgoing SA much match the key of the incoming SA on the other endpoint and vice-versa.

The key is a string of hexadecimal digits. The source seems to indicate that the key can be any length up to 512 bits, which is 128 hexadecimal digits. You must supply an even number of digits.

A network interface must be referenced using an if statement. It tells pipsecd the name of the tunnel device and the SPI to use for incoming and outgoing packets.

All this goes into pipsecd.conf in the /usr/local/etc/ipsec directory.

You can also create a script called startup in the same directory. This script gets called by pipsecd when it comes up, and is a good place to put statements for configuring the tunnel interface. This script must be executable.

	#!/bin/sh
	#############################################################################
	# This script is executed by pipsecd, and can presumably be used to set the
	# IP parameters on a tun interface at startup.
	#############################################################################
	ifconfig tun0 198.140.175.1 192.168.0.1 netmask 0xffffffff
	route add 192.168.0.0/24 192.168.0.1

Starting pipsecd

The steps necessary to start up the tunnel are:

	/usr/local/sbin/pipsecd &

Obviously, I would prefer if pipsecd could put itself in the background.

Improvements

Dynamic IP Address

Well, when I picked ESP, it was primarily because I didn't want to deal with a bunch of extra bloat. PPTP and L2TP add in a bunch of extra layers and define control channels besides.

But now I see that bloat can be useful at times. My first application for a tunnel was to allow me to connect my home network to the office network and bypass a bunch of firewall limitations. But my network at home is on a cable modem, which gets a DHCP lease, and theoretically my IP address can change.

Nothing in the IPsec ESP protocol tackles dynamic IP address assignment. At first glance, I don't really see a solution unless I brew one up myself or choose another tunneling protocol.

What I really need is a IP-level tunnel that uses the ssh encryption and authentication protocols. ssh can do socket-level tunneling, but I really need much more than that. [Update: I eventually wrote something like this, called sshtun. I wouldn't exactly call it robust. More on that later.]

OpenBSD

I recently tried to set up an IPsec a tunnel between a FreeBSD box using pipsecd and an OpenBSD 3.2 box with it's built in IPsec support. Bad news, data sent by pipsecd causes OpenBSD to crash. pipsecd sends empty packets for some reason, apparently as some kind of keepalive mechanism. OpenBSD doesn't expect this, so when it decrypts the packet, it wants to stuff an empty packet down the protocol stack, at which point it panics.

I tried making pipsecd not send empty packets. My OpenBSD box isn't crashing anymore, but at some point the tunnel seems to become unusable. I guess pipsecd really needs that keepalive mechanism. I guess my next step is to add the appropriate check in the OpenBSD kernel, but if it silently drops those packets, will pipsecd get what it needs?