Building Tunnels with VTun
Arthur Donkers
It has been a while since I wrote an article for Sys Admin. A lot of things have happened in that time -- business has been expanding, and Linux has definitely become mainstream. For my company, this meant that we grew out of our original office space and had to rent a new office to accommodate more people. And, since we did not want to drill holes in the (concrete) walls for the network cabling, we had to think of something else.
Wireless LAN After some searching on the Internet, I ran into a wireless LAN system that operates at a frequency of 2.4 GHz. This system consists of two PCMCIA cards, plus two PCMCIA-to-ISA adapters, that you can add to your server or laptop computer. These cards emulate a normal Ethernet adapter and work both under Linux and Microsoft. After some initial testing, these cards were able to make a reliable connection between our two offices, with an acceptable throughput.
However, using these cards causes a security problem. They communicate through the airwaves, free for anyone with a scanner (or a similar card) to pick up the signal and tune into the conversation. To solve this problem, a standard has been set that will encrypt the wireless communication, called WEP (Wireless Encryption Protocol). Different cards can join in by using the proper ESSID (a sort of shared secret that everyone must use to be able to communicate to each other). Being the paranoid people we are, we did not like this WEP thing, especially since even the 128-bit cards can be freely exported. So, we secured the communications ourselves. Besides, what's more fun than experimenting with new things under Linux?
Tunneling Through After some Internet research, we decided to lay an encrypted IP tunnel on top of the wireless connection. This would secure communications between our offices, without sacrificing network functionality. So, first I will explain what IP tunneling is all about.
Typically, on an IP network you communicate by directly addressing the party you want to talk to. So if, for instance, you wish to send an email to someone on the Internet, the following things happen:
You compose a message.
You give this message to your MTA (Mail Transfer Agent).
This MTA will ask DNS where he can find the mailserver for the destination.
Using the routing on the local machine, the MTA will connect to the mailserver and deliver the message.
This scenario applies to many other services as well (http, telnet, ftp), in the same way. It is just like sending an envelope to someone, directly to their address. Directly addressing your counterpart is clean and simple; however, there are some problems related to this (some of which are resolved in IPv6, but that's a different article). Two of the main problems are:
If you want to secure the communications, each protocol (server and client) must resolve that for themselves -- examples of these are SSH and SSL. Both of these added encryption and authentication functionality to the software for this.
If you want to address a system you cannot reach directly, you have another problem. Suppose you are working at a client's site, and you want to read your company email. You must contact your internal mailserver, but this machine has been secured by using a firewall, so you cannot contact it.
Both of these problems can be resolved with a tunnel. The best way to describe this is by saying that you put the envelope with the message into another envelope, addressed at the central mail dispatch of a company. This mail dispatch will pick up that envelope, open it, and send the original envelope on to the final destination.
The same thing happens with an IP tunnel. Your original IP traffic is encrypted, including all header information, and stored as a data payload into another IP connection (the tunnel). This IP connection carries your data to the other side, where it is decrypted and sent to its original destination (based on the headers in the original message).
Normally, this tunneling is done by the kernel itself, because it involves reassembling encrypted IP packets. Here's the process:
The kernel receives an IP packet.
The kernel recognizes that it is a special tunneling packet (mostly done by means of the protocol field. I think Cisco uses protocol #47 to indicate an IP tunneling packet).
Gets the payload from that packet.
Decrypts it (if necessary) into the original packet.
Sends the original packet on its way.
Sending tunneling packets follows the same flow, in reverse order:
The kernel receives an IP packet (mostly done on a special interface).
It encrypts the complete packet.
It builds a new IP packet containing the encrypted original as data payload.
Sends this new packet, with a special protocol ID, to the other side.
The kernel is normally assisted by a daemon that will do the authentication and determine the encryption key used between the different parties.
Tunneling the Linux Way: Getting and Building VTun For Linux, there are a number of tunneling packages available. If you visit freshmeat.net and search for tunnel, you get a list of different packages that build a tunnel one way or another. Especially for the 2.2.x (and 2.4.x) kernels, there are a number of tools available. There is a TAP device available, which allows you to manipulate raw IP packets in a user space program (e.g., a daemon or another program).
After doing some research into the different packages, we chose VTun (virtual tunnel) because it is clean and simple and also works under OpenBSD and other UNIX implementations. You can find the home page of VTun at:
http://vtun.sourceforge.net
The VTun software (derived from VPPP by Maxim Krasnyansky) consists of two parts, the universal TUN/TAP driver and the VTun software itself. Both have excellent documentation on how to build and install the software. VTun does not quite follow the tunneling model described above. It uses its own daemon that will do the encryption and authentication. This daemon will use the Linux TUN/TAP devices to receive and send the IP traffic. The general idea, however, remains the same.
If you follow these instructions, you should be able to build the software yourself. You may need to (possibly) recompile your kernel so it supports TUN and TAP. (TUN is a device for tunneling IP frames, and TAP is a device for tunneling Ethernet frames). You should install the TUN/TAP driver first (mind your kernel version), so you use the proper driver with Vtun. Both kernel options are shown below:
IP: tunneling (CONFIG_NET_IPIP) [M/n/y/?] ?
CONFIG_NET_IPIP:
Tunneling means encapsulating data of one
protocol type within another protocol and
sending it over a channel that understands
the encapsulating protocol. This particular tunneling driver
implements encapsulation of IP within IP, which sounds kind of
pointless, but can be useful if you want to make your (or some
other) machine appear on a different network than it physically
is, or to use mobile-IP facilities (allowing laptops to
seamlessly move between networks without changing their IP
addresses; check out
http://anchor.cs.binghamton.edu/~mobileip/LJ/index.html).
Saying Y to this option will produce two modules ( = code which
can be inserted in and removed from the running kernel whenever
you want). Most people won't need this and can say N.
Universal TUN/TAP device driver (CONFIG_TUN) [M/n/y/?] ?
CONFIG_TUN:
If you say Y here and create a character special files /dev/tun0
with major 90 and minor 0 and /dev/tap0 with major 90 and minor
128 using mknod (man mknod), you will be able to have a user
space program read and write raw IP or Ethernet frames from/to
that special file. tun0 and tap0 can be configured with ifconfig
and route like any other network devices but they are not
connected to any physical media; everything written by the user
to /dev/tun0 and /dev/tap0 is treated by the kernel as if it
had come in from a physical interface to the device tun0 or
tap0; everything the kernel wants to send out over the device
tun0 or tap0 can instead be read by the user from /dev/tun0 and
/dev/tap0. Please read the file Documentation/networking/tun.txt
for more information.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). The module will be called tun.o If you want to compile it
as a module, say M here and read Documentation/modules.txt. If
you don't know what to use this for, you don't need it.
It is a good idea to build these as a loadable kernel module to avoid recompiling the whole kernel when you want to change the version of your TUN/TAP driver. You can even have the module autoload by adding it to the /etc/conf.modules file.
After compiling the TUN/TAP driver, you can also compile the VTun software itself. You will end up with a vtund daemon program. After compiling all software, you should boot your new kernel.
Configuring VTun Now that the easy part is done, you need to configure VTun. This is done through a number of simple steps. First, you need to load the tun module so you can create a tunneling Ethernet interface. You can simply insmod the module or have it autoloaded. This is how it looks on our machine:
[root@gw /root]# lsmod
Module Size Used by
tun 2396 1
hisax 135080 2
isdn 85024 2 [hisax]
ray_cs 24572 1
ds 5740 1 [ray_cs]
i82365 22640 1
pcmcia_core 39464 0 [ray_cs ds i82365]
The tun module is the one from the TUN/TAP package. (Note that the ray_cs, ds, i82365, and pcmcia_core are used by the wireless LAN card and the rest are for our ISDN card).
After you have loaded the tun module (and possibly others), you can configure the Ethernet interfaces and routing in the normal way. Depending on your network, you get something like this:
eth1 Link encap:Ethernet HWaddr 00:00:8F:48:84:5E
inet addr:192.168.1.2 Bcast:192.168.1.3 \
Mask:255.255.255.252
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6332560 errors:0 dropped:0 overruns:0 frame:20736
TX packets:5736741 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
Interrupt:3
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:3924 Metric:1
RX packets:219222 errors:0 dropped:0 overruns:0 frame:0
TX packets:219222 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
tun1 Link encap:Point-to-Point Protocol
inet addr:192.168.2.2 P-t-P:192.168.2.1 \
Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1450 Metric:1
RX packets:5170215 errors:0 dropped:0 overruns:0 frame:0
TX packets:4211139 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:10
In our setup, eth1 is the wireless card and tun1 is the tunneling interface that communicates over this card. (The interface over which the tunneling software communicates depends on the routing and location of the VTun daemon on the other side. More on this later.)
Combined with the routing below, this completes the basic networking setup.
[root@gw /root]# netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
192.168.2.1 0.0.0.0 255.255.255.255 UH 0 0 0 tun1
192.168.1.0 0.0.0.0 255.255.255.252 U 0 0 0 eth1
127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
0.0.0.0 192.168.2.1 0.0.0.0 UG 0 0 0 tun1
You now have a secure connection running over your wireless network. Running tcpdump on the different interfaces will show you that the data is really encrypted. By using ping, I will demonstrate this below (remember that ping fills its databuffer with a bytecounter incrementing in steps of 1).
Running tcpdump on tun1, the input interface for traffic to be tunneled yields:
[root@gw /root]# ./tcpdump -i tun1 -lenx
Kernel filter, protocol ALL, datagram packet socket
tcpdump: listening on tun1
08:02:23.869025 > 0:0:0:0:0:0 0:0:0:0:0:0 ip 98: 193.78.174.193 > \
193.78.174.34: icmp: echo request
4500 0054 7e50 0000 3f01 1dd8 c14e aec1
c14e ae22 0800 0601 703b 0000 7eaf 7239
9ad7 0b00 0809 0a0b 0c0d 0e0f 1011 1213
1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
3435 3637
08:02:23.882594 < 0:0:0:0:0:0 0:0:0:0:0:1 ip 98: 193.78.174.34 > \
193.78.174.193: icmp: echo reply
4500 0054 a785 0000 fe01 35a2 c14e ae22
c14e aec1 0000 0e01 703b 0000 7eaf 7239
9ad7 0b00 0809 0a0b 0c0d 0e0f 1011 1213
1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
3435 3637
08:02:24.860996 > 0:0:0:0:0:0 0:0:0:0:0:0 ip 98: 193.78.174.193 > \
193.78.174.34: icmp: echo request
4500 0054 7e56 0000 3f01 1dd2 c14e aec1
c14e ae22 0800 3f20 703b 0100 7faf 7239
5fb8 0b00 0809 0a0b 0c0d 0e0f 1011 1213
1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
3435 3637
08:02:24.874011 < 0:0:0:0:0:0 0:0:0:0:0:1 ip 98: 193.78.174.34 > \
193.78.174.193: icmp: echo reply
4500 0054 a786 0000 fe01 35a1 c14e ae22
c14e aec1 0000 4720 703b 0100 7faf 7239
5fb8 0b00 0809 0a0b 0c0d 0e0f 1011 1213
1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
3435 3637
As you can see from the data part of tcpdump (look at the echo-requests), the data is nicely incremented each byte. If you run tcpdump on eth1 (the wireless interface that carries the tunneled data), you will see this:
[root@gw /root]# ./tcpdump -i eth1 -lenx
Kernel filter, protocol ALL, datagram packet socket
tcpdump: listening on eth1
08:03:29.856228 > 0:0:0:0:0:0 0:0:8f:48:84:5e arp 42: arp who-has \
192.168.1.1 tell 192.168.1.2 (0:0:8f:48:84:5e)
0001 0800 0604 0001 0000 8f48 845e c0a8
0102 0000 0000 0000 c0a8 0101
08:03:29.862493 < 0:0:8f:48:fd:f1 0:0:0:0:0:1 arp 64: arp reply \
192.168.1.1 is-at 0:0:8f:48:fd:f1 (0:0:8f:48:84:5e)
0001 0800 0604 0002 0000 8f48 fdf1 c0a8
0101 0000 8f48 845e c0a8 0102 b084 c14e
ae31 0000 0000 0000 c14e ae32 0000 f031
97a0
08:03:33.978459 > 0:0:0:0:0:0 0:0:8f:48:84:5e ip 140: \
192.168.1.2.socks > 192.168.1.1.vtun: udp 98
4500 007e 7f85 0000 4011 7796 c0a8 0102
c0a8 0101 0438 1388 006a b676 0060 5ec0
343d 6feb b64e 1c0f 5b14 66ab 2fb7 e783
9868 1773 fe8f a7bd 00d8 1e48 f246 f0fd
c6ce 1515 48c1 6a29 aefb 7faf d874 1c97
06fa 37f0 f7f2 300d b505 48f3 a9e3 8ec0
f671 1888 0ada 1f23 0f9d ca42 1ff1 6ad0
87ca 8283 75f4 71db 24bd cdcc d411
08:03:33.989318 < 0:0:8f:48:fd:f1 0:0:0:0:0:1 ip 144: \
192.168.1.1.vtun > 192.168.1.2.socks: udp 98
4500 007e 3ea4 0000 4011 b877 c0a8 0101
c0a8 0102 1388 0438 006a 9d96 0060 5ec0
343d 6feb b64e 4d95 f12c 4129 bc65 4ba1
132f 3e0e 6433 4703 be60 5beb d6c7 f0fd
c6ce 1515 48c1 6a29 aefb 7faf d874 1c97
06fa 37f0 f7f2 300d b505 48f3 a9e3 8ec0
f671 1888 0ada 1f23 0f9d ca42 1ff1 6ad0
87ca 8283 75f4 71db 24bd cdcc d411 fcc8
0918
08:03:34.973114 > 0:0:0:0:0:0 0:0:8f:48:84:5e ip 140: \
192.168.1.2.socks > 192.168.1.1.vtun: udp 98
4500 007e 7f89 0000 4011 7792 c0a8 0102
c0a8 0101 0438 1388 006a e34f 0060 5ec0
343d 6feb b64e 6ff7 f215 27ff 05f9 56a4
fd2f e44b 2297 c613 3ffe a08d eaaf 126e
1ae4 677d c2f4 aece 3bc6 5021 a826 1c97
06fa 37f0 f7f2 300d b505 48f3 a9e3 8ec0
f671 1888 0ada 1f23 0f9d ca42 1ff1 6ad0
87ca 8283 75f4 71db 24bd cdcc d411
All data will be gibberish, and you won't even recognize the ICMP protocol. You only see data between the IP addresses of the wireless interface. (The fact that one of them uses the socks port has nothing to do with the tunneling. The client daemon (which runs on 192.168.1.2) has used the first free port, which happened to be port 1080, the socks port. The VTun port is the service port of VTun when running in server mode.
You now have a running encrypted tunnel. The last thing to know is how to configure the VTun daemon.
Configuring vtund daemon For the tunnel to be established, you must run the daemon as a server on one machine and as a client on the other. The reason for this is mainly to have one daemon listening for connections from another one. If you would run vtund on a firewall, you would configure that daemon as a server. Clients who wish to make a connection run the daemon in client mode and make that connection when necessary. From the process listing, you can see whether the daemon runs in server [s] or client [c] mode:
ps ax
25677 ? S< 397:04 vtund[s]: wireless2 tun tun0
ps ax
7577 ? S< 261:28 vtund[c]: wireless2 tun tun1
Both daemons use a configuration file; you can specify this file on the command line when starting the daemon. If not specified, it will look for the default /etc/vtund.conf. I have made a separate file for the client and server configuration. Thus, you can change the behavior of the daemon, simply by giving it a different configuration file.
The file for the server looks like this (I changed the password for obvious reasons):
options {
port 5000; # Listen on this port.
# Path to various programs
ppp /usr/sbin/pppd;
ifconfig /sbin/ifconfig;
route /sbin/route;
firewall /sbin/ipchains;
}
# TUN config.
wireless2 {
pass SecretPassword; # Password
type tun; # IP tunnel
proto udp; # UDP protocol
comp lzo:9; # LZO compression level 9
encr yes; # Encryption
keepalive yes; # Keep connection alive
up {
# Connection is Up
ifconfig "%% 192.168.2.1 pointopoint 192.168.2.2 mtu 1450";
};
}
The general options apply to all configuration blocks in the file. You can have one server serve more than one client. Each client has its own configuration block, and the options part applies to all blocks.
The port in the options block says on which port the daemon listens for new connections from clients. Once the connection is established, it will fork an active daemon that will communicate over that port with the connected client. The other items are locations of different programs used to configure interfaces and routing when a client connection is made.
The wireless2 block is a block for a client that connects under the wireless2 name. It states the password (SecretPassword) that is also used by the client for authentication purposes. This password is never sent across the line, it is only used in a challenge-response mechanism.
The type clause tells what kind of tunnel is established. Typically, you will use a tun type, since this is able to carry all types of IP traffic. You can also select ether to build an Ethernet tunnel (so you can use the tunnel as a kind of bridge), or tty to make a tunnel over a serial connection, or pipe to make a tunnel over a pipe. Typically, you would set this to tun.
The proto clause tells vtund which protocol to use for communication to the other tunnel. Since UDP has the least overhead, you would usually choose udp. The vtund will make sure all data sent is also received.
The encr and comp will tell vtund to encrypt and compress the traffic. You can unset these, but that will mean your tunnel is no longer secure.
The keepalive clause will make sure the connection remains active if, for a longer period of time, you have no actual data traffic.
The up clause will tell vtund what to do once the wireless2 client makes a connection to the daemon. You can specify all kinds of commands here, even IPchains statements to close down your firewall.
The configuration file on the client side looks similar to the server:
options {
port 5000; # Connect to this port.
persist yes; # Persist mode
timeout 60; # General timeout
# Path to various programs
ppp /usr/sbin/pppd;
ifconfig /sbin/ifconfig;
route /sbin/route;
firewall /sbin/ipchains;
}
# TUN example. Host 'cobra'.
wireless2 {
pass SecretPassword; # Password
device tun1; # Device tun1
up {
# Connection is Up
# Assign IP addresses.
ifconfig "%% 192.168.2.2 pointopoint 192.168.2.1 mtu 1450";
};
}
You see that the password is included here as well; the other options are set by the serving daemon.
Conclusion By using the VTun software, you can establish a tunnel over almost any network connection you have. And although it uses its own protocol for that, it is available in source and for a number of different platforms. Checking with tcpdump reveals that all tunneled traffic is not sent in the clear and protects from eavesdropping.
About the Author
Arthur Donkers graduated from the Delft University of Technology with a degree in Electrical Engineering and a major in Computer Architecture. Since then he has worked for a number of major software houses in the Netherlands. His primary field of interest is in datacommunications, especially the security aspects involved. He now is the founder and owner of Le Reseau, a company specializing in security-related issues for UNIX, OpenVMS and Windows NT, and the application of Linux in corporate environments. |