Dec 062004

Secure Your Wireless with IPsec

This article originally appeared on O’Reilly ONLamp.

Wireless access is all the rage. Wireless this, wireless that. It’s everywhere. Hot spots are turning up everywhere. Many are free. Many are totally unsecured. There are several in my neighbourhood. I have no idea who is running them, but at least one is wide open. This article will show you one method for locking down your wireless network so that nobody but you can use it. This approach will take you beyond WEP and MAC address filtering, both of which are a good start, but have known exploits. This website already contains a few articles on IPsec. This article expands upon that foundation and demonstrates an easy method for securing your WAP (Wireless Access Point).

I will be using FreeBSD 4.10-STABLE for this excursion. Please keep your hand and legs within the vehicle at all times. In case of emergency, please follow the directions of your crew. They know what to do.

While writing this article, I have assumed the following

  • You have wireless working on your laptop.
  • You have a gateway already set up which will link your wireless and wired subnets.
  • Your WAP is set up:
    • you have the SSID set
    • traffic is flowing
    • you changed the default admin password

Why bother with wireless?

Wireless is practically wide open for anyone with a laptop, a wireless card, and the appropriate set of tools. WEP can be defeated. MAC addresses can be sniffed and spoofed. In short, you need to go to the next level. IPsec.

If wireless is so risky, why use it?


Wireless is convenient. There are no cables to run. Anyone can pop down to the local Future Shop, buy a wireless access point plug it in, turn it, and start surfing the 802.11 information super highway. Does this sound familiar? It should. I did it and I wrote about it. I did the right thing. I filtered by MAC address. I turned on WEP. But then I turned off WEP when I had trouble… Yes, I was vulnerable. But I wasn’t compromised.

As far as I know…

Since writing that article nearly 18 month ago, we have since moved to a new house. I’ve gone through the same process as last time time. Setting up a new rack. Running some cables through the walls. But this time I convinced myself that I was going to set up a secure wireless network. It took me a few hours, but I finally figured it out.

To answer my original question of this section, and to tie it in with my recent move, I use wireless so I don’t have to run cables. I want to be able to use the laptop in the living room, the dining room, or on the front step (that said, I’m actually typing this into my Windows XP workstation sitting at my desk in the basement, ssh’d into the new Antec box). But I am working on the wireless, I want to keep people off my private network keep prying eyes away from my communications. I can do that with IPsec.

Also in my mind, is my neighbour, I don’t know who, but there is a neighbour running a WAP nearby. Totally unsecured. I know how easy it would be for me to use their internet connection. I don’t want someone using mine.

What is IPsec?

IPsec is short for IP security. It is a set of protocols for securely exchanging packets at the IP layer. It is frequently used to implement VPNs. We can use the same approach to secure our wireless network. IPsec makes use of shared secrets to encrypt data. It also uses security policies to decide what types of traffic should be encrypted between what hosts. The configuration items outlined below will be sufficient to get you going.

FreeBSD specific details

This section outlines some of the details that are specific to IPsec on FreeBSD. Regardless of what operating system you wish to use, you will need an IPsec enabled kernel. Your kernel needs to have IPsec support. To do this, you need to have these directives in your kernel configuration file and then compile a new kernel:
options IPSEC         #IP security
options IPSEC_ESP     #IP security (crypto; define w/ IPSEC)
options IPSEC_DEBUG   #debug for IP security
I haven’t actually used the features of IPSEC_DEBUG, but it’s there if I need it. NOTE: If you are running 5.x (prior to 5.3-RELEASE), you should use FAST_IPSEC instead of the directives mentioned above. You will also need to remove INET6 as it is not supported by FAST_IPSEC. You can have your IPsec database set up at boot time by adding this directive to /etc/rc.conf:
That directive will load your IPsec configuration directives from /etc/ipsec.conf (the actual filename is also configurable using ipsec_file="/your/file/here". I will give you examples for that file later in this article.

Let’s walk first, then run

I’m a big believer in starting small, and working your way towards your goal. For my testing, I first tried IPsec over my wired network, then moved it over to the wireless network. You may find this strategy useful too. It allows you to concentrate on the IPsec portion of the problem, get that working, then concern yourself with any wireless issues. After you get the IPsec running properly, you can remove the wire, and start using 802.11 instead. For this testing, I created a new gateway and put two NICs into the box. This box does NAT using ipnat and will use ipf as a firewall. By placing the entire wireless network in a separate subnet, it is easier to manage. If necessary, the entire subnet can be disconnected by unplugging a single cable or powering off the WAP. Your NAT box will want to do some forwarding of packets. I recommend use of ipf and ipnat. I also use ipmon. I have these entries in /etc/rc.conf:
Firewall rules and NAT rules are beyond the scope of this article but those two links should give you a running start.

IPsec encryption – a short introduction

IPsec can create a point to point tunnel between two hosts. The data will be safe from prying eyes (i.e. it will be encrypted) and the gateway will not accept packets which have been modified, since they won’t have an authentic signature (i.e. the data will be secured). IPsec can also be configured to secure traffic between two networks or a network and a gateway. There are other configuration options available, but I am going to concentrate on just network, not point to point. The key point to realize is that IPsec cannot exist on its own. You need to have IPsec at both ends of the communication. You cannot just slap IPsec onto your laptop and expect it to work everywhere you go. This is why I have decided to create a wireless gateway. That gateway is the point through which all my wireless traffic will flow.

Network diagram

The following diagram (created with Xfig) illustrates my wireless network. My laptop sits at and communicates over wireless (802.11) to my WAP. The WAP is connected to a dedicated gateway box (via a hub) which sits between the WAP and my LAN.

wireless network Any traffic coming in over the wireless network must pass through the WAP and then the wireless gateway. This gateway has two NICs (one at and the other at These are conventional wired NICS. There is no WIFI in this gateway (but there is no reason why there could not be). I have chosen to use a WAP instead. The WAP will be plugged into a HUB, and on the gateway will be plugged into the same HUB. The other NIC will be plugged into the main LAN.

The IPsec database

IPsec uses a database to decide how to treat traffic. The database contains the rules on what traffic to encrypt, and how to encrypt it. There are two main types of rules: Policy and Association. The Security Policy Database (SPD) determines what traffic should be handled by IPsec. The Security Association Database (SAD) specifies how that traffic should be encrypted. The main tool for manipulating the database is setkey(8). I will show you one way to use that tool later. Usually, you place these rules in /etc/ipsec.conf. You will find rule examples in the next section.

Creating the network tunnel

These rules ensure that all traffic between the network ( and the gateway ( is encrypted. We will use ESP (Encapsulating Security Payload) as found in RFC 2406. This ensures that nobody can read your data.

rules on the laptop

add esp 691 -E rijndael-cbc "1234567890123456";
add esp 693 -E rijndael-cbc "1234567890123456";

spdadd any -P out ipsec esp/tunnel/;
spdadd any -P in ipsec esp/tunnel/;
The first two rules (add) are SAD entries. The next two rules (spdadd) are SPD entries. The add items set up the encryption keys for communication between the two computers. You will want to use different keys once you get started (actually, you won’t need keys if you use IKE). The values shown are just to keep things easy. The spdadd items set up the actual tunnel between the two computers. In brief, the above directives mean:
  1. Between and, use the index 691, the encryption algorithm known as rijndael-cbc, and use a shared secret of “1234567890123456”.
  2. Similarly, in the other direction between and, use the index 693, the same encryption algorithm, and the same shared secret.
  3. All communication between the network ( and anywhere else ( going out, must (require) go through a tunnel.
  4. In the other direction, communication between anywhere else ( and the network (, must (require) come from the tunnel.

rules on the gateway

You will notice that the rules for the gateway are very similar to the laptop rules and also slightly symmetric.
add esp 691 -E rijndael-cbc "1234567890123456";
add esp 693 -E rijndael-cbc "1234567890123456";

spdadd any -P in ipsec esp/tunnel/;
spdadd any -P out ipsec esp/tunnel/;
You can add these rules manually using
setkey -c
and then copy/paste the rules from above (after making adjustments so they refer to your IP addresses, not mine). To exit, press control-D. When testing, I actually keep it running and copy/paste the commands directly. I use these commands to clear out existing database entries before adding new ones.
The flush command clears out the SAD entries. The spdflush command clears out the SPD entries. In the policy statements (spdadd), the second line above states that all traffic from to anywhere requires ESP. The fourth line states that all traffic from anywhere to also requires ESP. Combined, these two directives ensure that all traffic from anywhere to anywhere (on this network) must be via ESP. While I was testing these rules, I kept them on a local website. That made it easier to copy/paste the rules from the browser. I’m not suggesting that you publicly publish your rules. This is just a debugging tool. Mind you, the only parts you need to keep secret are the keys (e.g. 1234567890123456). You can place your rules in /etc/ipsec.conf from where they will be loaded at boot time if you have ipsec_enable="YES" in your /etc/rc.conf. When loading the rules, you’ll need to coordinate them. I sat at the gateway console with my laptop beside me. That way, if I messed up the rules, I could reset them without moving. The rules provided worked for me. They should work for you too. If they don’t, go back to square one and ensure that your rules are correct. Ensure that the subnet and the IP addresses are what they should be. After you implement these rules, all traffic heading towards the gateway which is not ESP, will be rejected. Furthermore, anyone attempting to communicate with the gateway must have the shared secrets. Change the secret, nothing passes the gateway.

Confirming traffic

By this point, you have IPsec on both machines, and you have set the IPsec database rules. You have traffic flowing. Now you want to confirm, that yes, everything is encapsulated and not in clear text. Here is how I did that. On my wireless gateway, the IP address is assigned to dc0. All traffic from the laptop will be coming in on that NIC. I issued this command to view that traffic:
# tcpdump -ni dc0 not esp
tcpdump: listening on dc0
13:40:25.651640 > xid:0xebd53d39 [|bootp] [tos 0x10]
13:40:25.656090 > xid:0xebd53d39 Y: S: [|bootp] [tos 0x10]
The above is dhclient starting up on the laptop. The following is a bit of arp traffic. If all you see via tcpdump is stuff like this, then you’re good to go.
13:42:18.225304 arp who-has tell
13:42:18.225450 arp reply is-at 0:32:91:32:91:32
If all you see is arp, then you’re good to go. You should see what the IPsec traffic looks like. Have a look. Shorten the command above to this:
# tcpdump -ni dc0
tcpdump: listening on dc0
13:44:34.371866 > ESP(spi=0x000002b5,seq=0xfe)
13:44:34.385237 > ESP(spi=0x000002b3,seq=0xec) (frag 368:1480@0+)
13:44:34.385339 > esp (frag 368:48@1480)
13:44:34.387672 > ESP(spi=0x000002b3,seq=0xed) (frag 369:1480@0+)
13:44:34.387775 > esp (frag 369:48@1480)
13:44:34.390066 > ESP(spi=0x000002b3,seq=0xee) (frag 370:1480@0+)
13:44:34.390165 > esp (frag 370:48@1480)
13:44:34.390996 > ESP(spi=0x000002b3,seq=0xef)
13:44:34.393155 > ESP(spi=0x000002b3,seq=0xf0) (frag 372:1480@0+)
13:44:34.393260 > esp (frag 372:48@1480)
13:44:34.394641 > ESP(spi=0x000002b5,seq=0xff)
13:44:34.396986 > ESP(spi=0x000002b5,seq=0x100)
13:44:34.398044 > ESP(spi=0x000002b3,seq=0xf1) (frag 373:1480@0+)
13:44:34.398142 > esp (frag 373:48@1480)
The above tcpdump is of http traffic as my laptop access my development copy of FreshPorts. Here is what a ping looks like:
13:45:39.886113 > ESP(spi=0x000002b5,seq=0x118)
13:45:39.887436 > ESP(spi=0x000002b3,seq=0x10a)
13:45:40.898972 > ESP(spi=0x000002b5,seq=0x119)
13:45:40.900134 > ESP(spi=0x000002b3,seq=0x10b)
13:45:41.908735 > ESP(spi=0x000002b5,seq=0x11a)
13:45:41.909912 > ESP(spi=0x000002b3,seq=0x10c)
Note: is all ESP. The following is an example of traffic which does not use IPsec:
$ sudo tcpdump -ni fxp1
tcpdump: listening on fxp1
13:47:39.130953 > P 903783889:903783933(44) ack 4184487194 win 58400 (DF) [tos 0x10]
13:47:39.151794 > . ack 305831024 win 64160 (DF)
13:47:39.252127 > . ack 44 win 64028 (DF)
13:47:39.621526 > icmp: echo request
So there you go. All good. Nothing passes through the gateway unless it matches the rules. The shared secret is the key to this security. In the next section, I’ll show you how to keep those shared secrets changing so they are much harder to guess.

racoon likes to keep secrets

Instead of manually changing the shared secrets in your /etc/ipsec.conf file, you can keep one shared secret, and use the IKE protocol to negotiate a key. Racoon speaks IKE (ISAKMP/Oakley), which is a key management protocol. I installed racoon from the ports tree. I have supplied the configuration files from both my laptop, and the gateway. The only difference between the two files is that I instruct the gateway to listen on only one address (it has two NICs). Here is the diff if you are interested:
--- racoon.conf.laptop	Wed Sep 15 19:26:03 2004
+++ racoon.conf.gateway	Wed Sep 15 19:31:46 2004
@@ -33,6 +33,8 @@
 	#isakmp [500];
 	#admin [7002];		# administrative's port by kmpstat.
 	#strict_address; 	# required all addresses must be bound.
+	isakmp;
 # Specification of default various timer.
The configuration file tells racoon the main things it needs to know. One of the items is the pre-shared key file. Look for this directive:
# search this file for pre_shared_key with various ID key.
path pre_shared_key "/usr/local/etc/racoon/psk.txt" ;
From man racoon.conf:
Pre-shared key File
  Pre-shared key file defines a pair of the identifier and the shared
  secret key which are used at Pre-shared key authentication method in
  phase 1.  The pair in each lines are separated by some number of blanks
  and/or tab characters like hosts(5).  Key can be included any blanks
  because all of the words after 2nd column are interpreted as a secret
  key.  Lines start with #' are ignored.  Keys which start with ' are
  hexa-decimal strings.  Note that the file must be owned by the user ID
  running racoon(8) (usually the privileged user), and must not be accessi-
  ble by others.
So here is what is in my /usr/local/etc/racoon/psk.txt file on my laptop: MySecretValue
And here is the file from the wireless gateway: MySecretValue
With these values, racoon on my laptop knows that when it talks to (the gateway) it should use the shared key MySecretValue. Similarly, the racoon running on the gateway knows to use the same shared key when speaking to (my laptop). To start racoon, issue this command:
/usr/local/etc/rc.d/ start
But if you’re running a recent version of FreeBSD (e.g. 4.10-RELEASE), you’ll also need to have this entry in /etc/rc.conf:
Ensure that racoon is running on both the laptop and the gateway. Then you can remove the SAD entries from both machines, and racoon should negotiate a new set of keys.
# setkey -c
If that doesn’t work, try running racoon in the foreground (after first stopping the one running in the background):
/usr/local/sbin/racoon -F
But, it should just work.

Fun with keys – understanding what happens

I thought it might be interesting to clear out the SAD entries and see what happens when new keys must be negotiated. I started a ping running, and ran tcpdump while I issued this command:
# setkey -F
As you can see, the ping missed a few steps. That is understable.
[dan@laptop:~] $ ping -A
PING ( 56 data bytes
64 bytes from icmp_seq=0 ttl=63 time=4.880 ms
64 bytes from icmp_seq=1 ttl=63 time=4.847 ms
64 bytes from icmp_seq=2 ttl=63 time=5.126 ms
64 bytes from icmp_seq=3 ttl=63 time=5.209 ms
64 bytes from icmp_seq=6 ttl=63 time=5.468 ms
64 bytes from icmp_seq=7 ttl=63 time=4.838 ms
64 bytes from icmp_seq=8 ttl=63 time=5.270 ms
--- ping statistics ---
9 packets transmitted, 7 packets received, 22% packet loss
round-trip min/avg/max/stddev = 4.838/5.091/5.468/0.226 ms
[dan@laptop:~] $
Two pings went missing. They never reached the machine on the other side of the gateway. Here is some of the tcpdump traffic:
16:36:34.517794 > ESP(spi=0x02dc8063,seq=0x1d)
16:36:34.867830 > ESP(spi=0x02dc8063,seq=0x1e)
16:36:34.873592 > ESP(spi=0x0751ce23,seq=0x1d)
16:36:35.737921 > isakmp: phase 2/others ? inf[E]: [encrypted hash]
16:36:35.904270 > isakmp: phase 2/others ? oakley-quick[E]: [encrypted hash]
16:36:36.604941 > isakmp: phase 2/others ? oakley-quick[E]: [encrypted hash]
16:36:36.605565 > isakmp: phase 2/others ? oakley-quick[E]: [encrypted hash]
16:36:36.887859 > ESP(spi=0x01f4fcea,seq=0x1)
16:36:37.897889 > ESP(spi=0x01f4fcea,seq=0x2)
The lines which contain isakmp represent the two racoon daemons negotiating a new key. As an experiment, I turned off IPsec on my laptop, by commenting out the ipsec_enable line in /etc/rc.conf. Then I rebooted. It is interesting to note that I was still able to get an IP address from my DHCP server on the other side of the wireless gateway. However, I could not get through the gateway. Even simple pings to the gateway went unanswered. At this time, the firewall on the wireless gateway was allowing all traffic to pass. Therefore, the traffic was being rejected by the gateway because it was not IPsec. To get IPsec running again, I did this, while the ping was still running:
[root@laptop:/home/dan] # setkey -f /etc/ipsec.conf
[root@laptop:/home/dan] # tcpdump -ni wi0
tcpdump: listening on wi0
17:21:20.168434 > isakmp: phase 2/others ? oakley-quick[E]: [encrypted hash]
17:21:46.426485 > isakmp: phase 2/others ? oakley-quick[E]: [encrypted hash]
17:21:47.112010 > isakmp: phase 2/others ? oakley-quick[E]: [encrypted hash]
17:21:47.113115 > isakmp: phase 2/others ? oakley-quick[E]: [encrypted hash]
17:21:47.375549 > ESP(spi=0x02c19c7a,seq=0x1)
17:21:48.385549 > ESP(spi=0x02c19c7a,seq=0x2)
17:21:48.390088 > ESP(spi=0x041e48fb,seq=0x1)
17:21:49.395564 > ESP(spi=0x02c19c7a,seq=0x3)
17:21:49.399754 > ESP(spi=0x041e48fb,seq=0x2)
The first line populates the SAD,1 database, based upon the data within the file /etc/ipsec.conf. From there, racoon is left to negotiate a new key. It took some time (about 26 seconds), but racoon eventually succeeded. Immediately thereafter, the pings resumed. 1Actually, the command populates only the SAD because the file in question contains only add commands. The spdadd commands have been commented out.

DHCP server

I mentioned above that I could still get an IP address from my DHCP server that was running on my gateway. I had not previously mentioned this but I think it might be useful to you. Here are the basics. The rest you should be able to piece together yourself.

Installing dhcpd

To install the dhcp server, I did this:
cd /usr/ports/net/isc-dhcp3-server
make install clean

Starting at boot time

This will install /usr/local/etc/rc.d/ Remember to add dhcpd_enable="YES" to /etc/rc.conf or the server will not be started by the script. I also added dhcpd_ifaces="dc0" so that dhcpd would listen only on the one NIC, the one attached to the same hub as the WAP.

The configuration file

The ports installs /usr/local/etc/dhcpd.conf. It is full of examples, but here is what I’m using, slightly altered to protect the obvious:
default-lease-time 600;
max-lease-time 7200;

ddns-update-style none;

option domain-name "";

# this points to my local DNS server on the other
# side of the wireless gateway
option domain-name-servers;

default-lease-time 86400;
max-lease-time 86400;

# This is a very basic subnet declaration.

subnet netmask {
        option routers;
        range; # this is => (28)

        host {
                option dhcp-client-identifier "";
That fixed-address relates to the following entry in /etc/dhclient.conf:
send dhcp-client-identifier "";
This allows the laptop to tell the DHCP server who it is, and I use that to assign a specific IP address. Note: this method is convenient, but it is not necessarily secure. If you’re like me, and sometimes your laptop uses wireless, and sometimes it’s connected via wire, then you might want to give it a different IP address depending on where it is. I do that by having two DHCP servers. I’m sure someone will show us another method.

Is that enough?

Now that you have your wireless laptop connected to your LAN, and the traffic is not only encrypted, it is also secured. Nobody else can use your gateway unless they can guess the secret key. That’s not easy. The keys will be changed from time to time, so even if a key is guessed, there’s a new one coming along soon. The only thing you have to secure is the pre-shared secret. Don’t use what I’ve supplied. Come up with something odd. Even some random values. Pick some text from IRC. That should work. But are you being paranoid enough? I think for one of my next tasks, I will look for any unusual traffic coming on on the gateway, from any IP other than my laptop. Whole books have been written on intrusion detection, and that topic is well beyond what I can cover here. Enjoy.

  4 Responses to “Secure Your Wireless with IPsec”

  1. I might add that sl2tpd is a small, simple and easy-to-configure L2TP-IPSec tunnel server which might be added to get standard MacOSX and Winblows boxes to access the system, all with simple user+password authentication.

  2. You could consider putting a small, reliable (I’d say secure, but don’t mean https), httpd on the gateway. Set the gateways firewall to divert any wireless port 80 traffic to it, and drop all other non IPsec traffic.

    The httpd could either serve up appropriate IPsec configuration instructions (sans password) for guest’s laptops, or a JPEG of the buisness end of a 12 gauge, depending on your mood.

  3. Recently, I’ve been playing with OpenVPN [] in a number of environments and I have found it quite light to set up compared to most other IPSec configurations I’ve bumped into.

    The software is available on most of the major platforms (linux, win2000/XP, OpenBSD, FreeBSD, NetBSD, MacOS X, and Solaris) and there are GUIs for MacOS X and Windows.

    The reason I bring this is up is because I found that the OpenVPN tunnel is ‘secure’, the daemon offers and assigns IPs to clients with its own DHCP service. The service can be set up to operate in a bridging or routing mode as well. This may allow you to work with the internal services that require broadcast capabilities such as NetBIOS, etc.

    Authentication is handled with SSL Certificates that can be password protected, etc., ensuring that you’re really talking to you the server you think you’re talking to. While not directly applicable to your wireless security setup, OpenVPN appears to do quite well through NAT where I was having serious connectivity problems with IPSec and people roaming behind various access gateways at hotels, etc, etc.

    Finally, the complexity of IPSec and cost of dedicated clients for Windows made managing the infrastructure more work than I wanted. OpenVPN so far has been working well and according to a few papers out there (Check out, OpenVPN is well done and so far I’m inclined to agree.

    Anyhow, I thought I’d put another option out there on the table. I’ve personally deployed OpenVPN on OpenBSD out of familliarity and I’m quite pleased with the software thus far.

    Should you decide to try out OpenVPN, I’d like to hear about your experiences with it. 🙂

    — Wyness