4.8. gtunnelSometimes, we want to build a type of tunnel that is not supported by the operating system, or we need to have finer control of the tunnel parameters than the operating system provides. In these cases, it is useful to be able to do our own encapsulation of packets as they leave the TCP/IP stack. In principle, we could do this by modifying the stack itself, but doing so has several disadvantages. First, it requires an intimate knowledge of the operating system's kernel, its data structures, and its support routines. More serious, unless the operating system's vendor can be persuaded to incorporate the changes into the base system, the modifications become a maintenance problem because they have to be reapplied with every new release of the operating system. Fortunately, many operating systemsincluding FreeBSD, Linux, and Solarisprovide a facility that allows us to do the encapsulation easily in a user-space program. Our examples will use the FreeBSD tunnel driver, but Linux and Solaris offer essentially identical services through their TUN/TAP drivers. We refer to all these facilities as "tunnel drivers." The tunnel drivers appear to the TCP/IP stack to be device drivers for a network interface device, such as an Ethernet card. Instead of encapsulating packets from the TCP/IP in, say, an Ethernet frame and passing the result to a physical device, the tunnel drivers deliver them to a user-space program, which performs further processing on the packet and then delivers it to the appropriate output device. Similarly, the user-space program can pass packets to the tunnel driver for delivery back up the TCP/IP stack. A typical use of a tunnel driver is illustrated in Figure 4.63, which shows a user application talking to the TCP/IP stack in the normal way. The packets that result move down the stack to the tunnel driver, which delivers them to a user-space program, labeled gtunnel, that performs further processing and delivers them to the output device. Figure 4.63. gtunnel and the Tunnel Driver
A typical use of the tunnel driver is illustrated by the FreeBSD PPP implementation. Rather than implement PPP in the kernel, as most other systems do, FreeBSD provides PPP functionality with a normal user-space application program called pppd. The pppd program communicates with the TCP/IP stack through the tunnel driver and with the outside world, typically, through a serial port. Thus, pppd encapsulates IP packets from the stack in PPP frames and delivers them to a serial port for transmission to the remote system. PPP frames from the remote system arrive at the serial port and are read by pppd, which strips off the PPP framing and delivers the resulting IP packet to the TCP/IP stack through the tunnel driver. Building a gtunnel.c SkeletonWe can write our own programs that provide whatever processing and encapsulation are needed to implement a particular type of tunnel. To make this easier, we use the gtunnel.c skeleton shown in Figure 4.64. By providing the startup, inbound, and outbound functions, we can flesh gtunnel out to a complete tunnel implementation. Figure 4.64. gtunnel Skeleton
As we see from Figure 4.64, our skeleton provides merely the main function. We build a complete tunnel implementation by providing a startup function (line 12) to perform any necessary initialization and set up communications with our peer, as well as outbound (line 30) and inbound (line 32) functions to handle packets from the stack and our peer. The main function
Building an IP-in-IP Tunnel with gtunnelTo illustrate the use of gtunnel, let's build an IP-in-IP tunnel like the one in Section 4.2, using the network configuration shown in Figure 4.65. The IP-in-IP tunnel will be built between the 192.168.1.1 interface on bsd and the 192.168.2.1 interface on laptop, with both interfaces assigned to the tunnel driver. The packets will actually follow the shaded path through the usual 172.30.0.0/24 network. Figure 4.65. Network Diagram for an IP-in-IP Tunnel
We begin fleshing out gtunnel by including extra header files (Figure 4.66) that we will need and by adding a define and global definition to the start of gtunnel.c. We will call our new file ipip.c. Figure 4.66. Header Files for ipip.c
The startup Function
The outbound Function
The inbound Function
If we examine the tcpdump capture of one of these pings, we see the expected encapsulation. Except for the addresses, it is identical to that from Section 4.2 (the outer IP header is set in boldface): 1 16:47:06.482086 172.30.0.1 > 172.30.0.6: 192.168.1.1 > 192.168.2.1: icmp: echo request (ipip-proto-4) 1.1 4500 0068 17db 0000 4004 0a74 ac1e 0001 E..h....@..t.... 1.2 ac1e 0006 4500 0054 17da 0000 4001 de7c ....E..T....@..| 1.3 c0a8 0101 c0a8 0201 0800 b033 463e 0000 ...........3F>.. 1.4 4aef 3041 945a 0700 0809 0a0b 0c0d 0e0f J.0A.Z.......... 1.5 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f ................ 1.6 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f .!"#$%&'()*+,-./ 1.7 3031 3233 3435 3637 01234567 We've seen that by adding as little as 52 lines of code to our gtunnel skeleton, we can build a functional tunnel. Obviously, we could improve our tunnel by including code to track the tunnel health, to worry about MTUs, to provide diagnostics and other features, and so on (see Exercises 4.6 and 4.7, for example). The point is that gtunnel provides an infrastructure on which we can build arbitrarily complex tunnels. |