2009-09-05

DD-WRT OpenVPN Persistent Routed Split Tunnel Howto

The purpose of this howto is to explain how to get OpenVPN working with a routed tun setup. The goal is to have a permanent point-to-point link connecting several networks together through a central VPN server that manages the routing table propagation. We don't want to NAT the routes, either, so this post explains how to disable that.

I used dd-wrt build 12533 (vpn generic) on a Linksys WRT310N v1.0.

Go here, and find your model: http://www.dd-wrt.com/dd-wrtv3/dd-wrt/hardware.html

Download the mini and vpn versions. You *must* install the mini version first, then upgrade from that to the vpn build. It has to do with how Linksys' firmware loader handles loading images over 3MB. The mini build overcomes this restriction, so get it installed first. There are plenty of documents on the dd-wrt site and on the net explaining this issue in detail, so I won't discuss it any further.

I'm going to assume that you're familiar enough with linksys and/or dd-wrt to get it installed and running, so I won't explain the process in detail, but here are the steps I generally take to prep my hardware:

Upgrade to latest Linksys firmware, then reset to factory defaults.

Install dd-wrt mini via web interface, then reset to factory defaults. (I use the 30-30-30 method here).

Install dd-wrt vpn via web interface, then reset to factory defaults.

The dd-wrt boards generally recommend doing a 30-30-30 reset on the system after each update, but I only do it after the initial dd-wrt load, then I use the factory reset software option when upgrading my firmwares.

If you don't know what the 30-30-30 reset method is, go google it. It's basically just holding down the reset button to do a hard reset (though diehards might disagree with me), so if you've ever restored a linksys to factory state with that little reset button in the back, that's pretty much it.

Ok, on with the configuration. At this point I'm going to assume that you have the vpn build installed and are ready to configure it.

For my configuration, the server is servicing 192.168.0.0/20. My network is 192.168.16.0/20 and there are other 192.168.x.0/20 networks hosted by other clients which will be connecting to the VPN as well. For simplicity sake I will only show how to configure a single client, but I'll touch on the points that are relevant to consider when handling multiple permanent VPN links in this scenario.

First, I configured my router (LAN-side) for the appropriate local subnet (192.168.16.0/20). Since my router gets a DHCP address on the WAN-side, I won't go into configuration here and will assume that your router is configured to reach the Internet.


Since OpenVPN is an SSL-based VPN we'll need to generate certificates. This is the most complicated part of the process. I'm not going to go into how to generate the server certificate, but I will explain how I generate my client certificates.

OpenVPN is going to be running on embedded hardware, and modifying the dd-wrt installation post-install is a bit of a pain in the butt, we're going to use certificates that don't have a passphrase so that the setup will be simpler. As always, keep your private key private.

I've named my router Crete, so let's generate a certificate:

> openssl genrsa -out crete.key
> openssl req -new -key crete.key -out crete.csr -days 730
> openssl ca -policy policy_anything -out crete.pem -infiles crete.csr

The data in the certificate doesn't matter *EXCEPT* for the CN field. This matters very much for reasons we'll get into later. Since my router is named Crete, I'm going to put "Crete" in my CN field.

Also, remember to *NOT* put a passphrase on your certificate, but you will need to provide the CA's passphrase to sign the certificate.

Here are the details for my signed certificate:

Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 5 (0x5)
Validity
Not Before: Sep 4 10:44:22 2009 GMT
Not After : Sep 4 10:44:22 2010 GMT
Subject:
countryName = US
stateOrProvinceName = UT
localityName = Murray
organizationName = OpenSEA
organizationalUnitName = Open1X
commonName = Crete
emailAddress = terry.simons@gmail.com
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
X509v3 Authority Key Identifier:
keyid:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX

Certificate is to be certified until Sep 4 10:44:22 2010 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Ok, we're ready to configure the VPN.

Go to Services->VPN and enable the OpenVPN Client.

Enter in the relevant information. For my purposes I'm using UDP TUN with LZO compression. Everything else is default.

You'll need to copy and paste in the certificates now. The CA certificate which was used to sign your client certificate, your client certificate, and your private key. My files are named cacert.pem, crete.pem, and crete.key. When you paste them into the UI, you *only* need the data in between (and including) the BEGIN and END lines.

Also, I include an extra line after the END just because I've seen some things get picky about certificates that way. I don't know if it's strictly necessary on this setup.

Go ahead and save this and head over to the Administrations->Commands window.

Here's the most simple setup:

# Accept any tunnel traffic destined for Crete
iptables -I INPUT 1 -i tun+ -j ACCEPT

# Accept any traffic leaving Crete and heading for any tunnel
iptables -I OUTPUT 1 -i tun+ -j ACCEPT

# Accept any traffic that needs to be forwarded to or from the tunnel on behalf of another host.
iptables -I FORWARD 1 -i tun+ -j ACCEPT

The above lines make it so we'll accept any input or output from the tunnel, and we'll forward anything that we need to that is tunnel related. The above rules are very insecure, but you can use them to troubleshoot if you have issues.

I'm going to go off of a stricter set of rules for my configuration:

Paste this in the Commands box:

# DROP any traffic coming from any tunnel that purports to be us.
iptables -I INPUT 1 -i tun+ -s 192.168.16.0/20 -j DROP

# Accept any other tunnel traffic.
iptables -I INPUT 2 -i tun+ -j ACCEPT

# Forward traffic through the tunnel for 172.16.16.0/24 hosts (this is our tunnel address space).
iptables -I FORWARD 1 -o tun0 -s 172.16.16.0/24 -j ACCEPT

# Forward traffic through the tunnel for any of our subnet.
iptables -I FORWARD 2 -o tun0 -s 192.168.16.0/20 -j ACCEPT

# Drop any other traffic trying to go through the tunnel.
iptables -I FORWARD 3 -o tun0 -j DROP





# Turn off NAT on the tunnel, since we want to route directly.
iptables -D POSTROUTING -t nat -o tun0 -j MASQUERADE

Note that if we add more tunnels to different places in the future, the above rules will prevent the other tunnels from talking to each other.

Click Save Firewall

The INPUT chain configuration says to accept packets inbound from the tunnel interface that are destined for the router (Crete), but to drop any other traffic. If you'd rather not have people poking at your router remotely you can leave this line out, but you'll probably still want to block anyone claiming to be 192.168.16.0/20 (or whatever your subnet is).

The FORWARD chain configuration says to accept packets that are destined to be forwarded on beyond Crete. This means the systems behind the router on 192.168.16.0/20, but not including Crete. We also explicitly include the tunnel interfaces (172.16.16.0/24) but exclude everything else. This prevents addresses we don't trust from using our tunnel.

Strictly speaking, at this point if your server is configured you should be able to establish a tunnel and pass traffic, but this wasn't good enough for me. The problem is that the default OpenVPN configuration on dd-wrt brings up a NAT that tunnels all traffic out the tunnel interface, but I want only traffic destined for systems on the other side of the tunnel to traverse the tunnel. Everything else should be routed out my WAN interface.

In order to accomplish this we turn off NAT with the final iptables line.
That's it. Now it's time to discuss the server configuration.

Here it is:

port 1194
proto udp
dev tun
ca /etc/ssl/CA/cacert.pem
cert /etc/ssl/CA/server_cert.pem
key /etc/ssl/CA/server_key.pem
dh /etc/ssl/CA/dh1024.pem
server 172.16.16.0 255.255.255.0
ifconfig-pool-persist ipp.txt 60
keepalive 10 30
comp-lzo
persist-key
persist-tun
status status.log
verb 3

client-to-client
client-config-dir ccd
route 192.168.16.0 255.255.240.0
route 192.168.32.0 255.255.240.0
route 192.168.64.0 255.255.240.0
route 192.168.96.0 255.255.240.0
route 192.168.128.0 255.255.240.0
route 192.168.192.0 255.255.240.0
route 192.168.224.0 255.255.240.0
route 192.168.240.0 255.255.240.0

push "route 192.168.0.0 255.255.240.0"
push "route 192.168.16.0 255.255.240.0"
push "route 192.168.32.0 255.255.240.0"
push "route 192.168.64.0 255.255.240.0"
push "route 192.168.96.0 255.255.240.0"
push "route 192.168.128.0 255.255.240.0"
push "route 192.168.192.0 255.255.240.0"
push "route 192.168.224.0 255.255.240.0"
push "route 192.168.240.0 255.255.240.0"

Let's break this down into manageable chunks:

Pretty self explanatory. These options mirror the client settings. UDP port 1194 with a tun interface.

port 1194
proto udp
dev tun

Server certificate configuration, again you'll need to figure this step out on your own:

ca /etc/ssl/CA/cacert.pem
cert /etc/ssl/CA/server_cert.pem
key /etc/ssl/CA/server_key.pem
dh /etc/ssl/CA/dh1024.pem

Next up we configure the tun server subnet. This is arbitrary. It's just a subnet used by the server to establish the point-to-point links. Just make sure it's an RFC 1918 address space (unless you want to use real space) that you aren't using for your clients.

server 172.16.16.0 255.255.255.0

Set up a persistent pool:

ifconfig-pool-persist ipp.txt 60

This just causes everybody to get the same tunnel addresses every time they connect. It's not strictly required for the setup unless you think you have a need.

The keepalive command dictates how a client handles a dropped connection. This means they'll try pinging the server every 10 seconds and if the connection is down for 30 seconds they will try to re-establish the link.

keepalive 10 30

Now we turn on compression. Again this mirrors the client configuration.

comp-lzo

I'm not sure what these persistent options do.

persist-key
persist-tun

Configure the status log and verbosity level:

status status.log
verb 3

And the rest of this is purely routing magic for our clients and server to be able to communicate with each other.

This is where the interesting things happen.

First, we need to let other clients communicate with each other:

client-to-client

And we also need to set up a client configuration directory:

client-config-dir ccd

The client configuration directory is where the CN that you configured in your certificate becomes relevant.

You need to create a file that is named whatever is in your certificate's CN field. My router is named Crete, so I'll create a file named Crete in the ccd directory. Inside the file is the following configuration line:

iroute 192.168.16.0/20

NOTE WELL: The iroute command *MUST* be in the client configuration directory file. I'll explain why in a moment, and yes, the route appears to be redundant because of the route 192.168.16.0/20 directive in the main server configuration, but it's not. I'll explain why.

Finally the route entries.

The normal "route" entries are route directives for the server. This tells the server to create routing entries for these networks. You might be wondering how the server knows where the other side of the route is. That will become clear in a moment.

route 192.168.16.0 255.255.240.0
route 192.168.32.0 255.255.240.0
route 192.168.64.0 255.255.240.0
route 192.168.96.0 255.255.240.0
route 192.168.128.0 255.255.240.0
route 192.168.192.0 255.255.240.0
route 192.168.224.0 255.255.240.0
route 192.168.240.0 255.255.240.0

Next, we push the routes out to all of the clients that connect.

What's actually going on here is that the openvpn server uses the client configuration directory entry to determine who owns which subnet. So in my case Crete owns the 192.168.16.0/20 subnet. The server uses that information to construct its local route. It also uses this information to know *NOT* to push the 192.168.16.0/20 route out to Crete when it does the push. This is important because if the route does accidentally get pushed to the client it will appear to have died. I thought I had found a bug in OpenVPN on dd-wrt when I first experienced this, but I realized that it was a misconfiguration on my part… so be careful here. If you've managed to lose connectivity to your router and you think this might be the cause, unplug it from the WAN, reboot it, and you should be able to talk to it again. You can disable the OpenVPN client (your certificates and configuration won't be purged) and you can plug the router back. Once you have your client configurations set up properly you should be able to connect.

Verify you can ping the remote tunnel endpoint and that they can ping you.

One other note about routes. You *MUST* ensure that any routers behind the respective networks have routes back to the VPN client for this to work. If your VPN client is your router, then you don't have to do anything extra. If you have enterprise-grade equipment servicing routes that are being pushed by the VPN server then the routers managing those networks need to route the various remote networks back to the VPN router.

2007-02-05

IEEE 802.1AB - LLDP and the OpenLLDP Project

One of the most useful tools in troubleshooting and maintaining a large-scale enterprise network is the Cisco Discovery Protocol CDP. Any network admin worth a grain of salt that has had exposure to Cisco gear in any capacity knows about this protocol. Unfortunately, CDP is proprietary to Cisco, which doesn't lend well to multi-vendor environments. Although, I know of one vendor (HP) that has licensed the protocol for use in their gear.

Other vendors have their own discovery protocols as well. Foundry uses the Foundry Discovery Protocol (FDP). Extreme has Extreme Discovery Protocol (EDP), Enterasys also has CDP, but the C stands for Cabletron... Nortel also has its own NDP. Any wagers as to what the "N" stands for?

As you might guess, none of these protocols interoperate with each other... or if they do it is limited at best. I have heard that Foundry can understand Cisco's Discovery Protocol, but not speak it. All of the protocols are roughly similar. They provide information about their upstream neighbors. Unfortunately, both sides need to speak the same protocol to be of any worthwhile purpose.

Enter IEEE 802.1AB. Also known as the Link Layer Discovery Protocol (LLDP). This standard, which was ratified in 2005, is intended to provide a vendor-neutral solution to the discovery protocol game.

Interestingly enough, many of the big players are beginning to push LLDP. HP has actually dropped support for the licensed Cisco Discovery Protocol in favor of LLDP. Extreme, Avaya, and Mitel are all coming on board too. Cisco even mentions LLDP on their website, though it's apparent by at least one whitepaper that they intend to push CDP in favor of LLDP for the foreseeable future.

Cisco's main argument is that LLDP isn't as robust as CDP... although, this shouldn't be a problem since LLDP specifies the ability to publish organizational extensions, which should allow Cisco (and others) to extend the protocol anyway they see fit. I suspect the real issue here is that Cisco doesn't want to spend the money required to untangle the mess they've made with CDP... but that's a story for another post. If you don't believe me, try turning CDP off in your network for a few months... but don't call me when you try to span a new VLAN and can't figure out for the life of you why your trunks aren't behaving.

LLDP is interesting not only for the inter-vendor aspects, but also because it defines a mechanism to identify end-stations. When I heard about LLDP I was extremely excited... The thought of being able to console into a switch and dump the interface information and see that a rogue Linux box is enough to make any network administrator squeal with delight.

It is for this very reason that Jason Peterson and I have formally begun work on an open source LLDP project called OpenLLDP. Our goal is to provide an LLDP agent for a minimum of Mac OS X, Linux, and Windows.

We've been hacking on the project for a few months now. The code in CVS is more a proof of concept than a functional agent at this point, but we're making steady progress. Some of the code was borrowed from the venerable Open1X project. Kudos to Chris Hessing for being an awesome mentor and friend the past few years. My Network-and-Code-Fu would be virtually non-existent without him.

Stay tuned for more news on the OpenLLDP project!

- Terry

2005-12-22

How to Learn : Mac OS X : AppleScript

So you want to learn AppleScript? You've seen it do some amazing things, or you're just curious about it's capabilities, but you're not sure where to look for more information. You could just google it. Maybe that's how you found this site. ;) Keep reading.

A colleague of mine, who is quite tech savvy approached me about Mac OS X after aquiring a new Powerbook.

He was mostly interested in learning about Mac OS X. Coming up to speed on a new operating system can be painful, especially when you don't know where to start. I threw out the names of a few applications that I thought were generally useful and asked him about what he was trying to accomplish.

Partway through our conversation he asked me "What do you know about AppleScript?", to which I replied that I had done a bit of HyperCard back in the "good old days", and a bit of AppleScript more recently. I asked him if he had anything specific in mind.

His asked, "Is AppleScript at all useful?".

This begs the question... "Well, what do you want to do with it?".

Most often I find that people simply say, "I don't know", when they should be asking "What CAN I do with it?".

My friend and colleague was asking me whether AppleScript was useful because he really didn't know anything about it. Without knowing what he wanted to do, I couldn't really answer his question. Instead of trying to give him examples of how AppleScript is widely useful and trying to bestow upon him the virtuous wonder that is Mac OS X ;), I introduced him to what AppleScript can do so he could decide for himself whether AppleScript was useless or not.

In this "How to Learn" article, I want to use the above example as a way to teach about a technology without ever looking at a code example. Looking at code might teach you how to ask the finder to open a new window, but it doesn't teach you how to find information... that's what this article is all about.

I still haven't found what I'm looking for!

My goal here is to point out what you need in order to make an informed decision about a specific technology - AppleScript in this case.

So let's get started.

What are you looking for?

Your answer can most likely be found in the Script Editor library. Well, what is Script Editor, and where is it located?

Script Editor is an Apple Application for composing AppleScripts. It has existed, in one form or another, since early versions of Mac OS.

Script Editor is located in "/Applications/AppleScript/" on Mac OS X by default.

First, an exercise that can certainly be used with any Apple-shipped products, and will also be the case for any decent 3rd party Mac OS X software:

Open Script Editor and check out "Help->Script Editor Help".

Apple's Help is an amazing reference utility that is often underutilized. It's much better than any other help system that I have ever seen.

The first help topic is "Learn about Script Editor", which will give you an overview of what Script Editor is, and what it can do. Consider it a first-step in learning whether AppleScript is useful. The help gives you some basics, as well as helpful hints on where to get more information.

The second AppleScript suggestion I have, and arguably the most important, helps answer that fundamental question posed earlier. What can AppleScript do? Now, this question can certainly be answered by looking at the language, but there is an easier way.

Chances that you will want to use AppleScript with another application in the system are good. In order to do that, you need to know what actions the application exports. It isn't super obvious where this information is kept, and it took me a little bit of time to dig the pearl out of the oyster of information that is the Internet.

Each AppleScriptable Application contains a set of actions, or verbs, that it recognizes. These actions, or at least the Apple specific ones, are stored in a library that is easily accessible (but not obvious) from Script Editor.

To access the library, open Script Editor and choose "Window->Library", or use the shortcut (Command-Shift-L). This library contains all of the information you need to interact with any of the AppleScript ready Applications. You can also add new libraries for other applications.

The AppleScript library for a given application should contain pretty much everything you need to know about scripting against that application. It's a complex, but succinct, application specific API reference.

That should be enough to get you started learning what AppleScript can do for you. If this article was helpful, please leave a comment!