ometimes you can stumble upon interesting discovers while actually looking for something else. That happened to me when I decided to try another Linux distribution besides my favorite one: Debian. I have used Debian since 1998 when I was a budding Java developer trying to install a Java Virtual Machine under Linux. Back then, Debian 2.1 (slink) was the only distribution that allowed me to do this. I've used Debian ever since and have become an experienced Debian user and administrator who enjoys solving a lot of usability challenges manually.
All along I have resisted the lure of other Linux distributions and I believe I did not miss much during these years. However, as Ubuntu increasingly became a worldwide phenomenon, I eventually decided to try it but didn't want to leave Debian behind. So I decided to use virtualization: I would run Linux (Ubuntu) on Linux (Debian).
Nowadays, virtualization solutions are everywhere; some run directly over BIOS, others under Linux or Windows. I discovered that even the Linux kernel offers a way to run itself with the User Mode Linux framework (UML). UML is a nested Linux instance (called guest) running inside another Linux instance (called host). The guest Linux will run in user space as a standard application, which prevents any operation in the guest from causing a crash in the host system.
The aim of this article is to present my experience building and running two separate UML instances of Ubuntu distributions. For Debian users like me, it can serve as a starting point for understanding UML's potential and exploring the recent Ubuntu 7.10 (Gutsy Gibbon) release without moving away from their familiar Debian environment.
UML Building Blocks
In order to run my Linux user space, I had to:
- Configure a Linux host system.
- Prepare a standard and complete Linux filesystem and then customize it.
- Compile the special executable Linux kernel to run inside the Linux host.
I used Linux kernel version 220.127.116.11, the stable one at the time of this writing. For the creation of the UML root filesystem, I needed the support of a loopback device and ext3 filesystems. So I entered this in the kernel configuration menu:
Device Drivers --->
[*] Block devices --->
<*> Loopback device support
File systems --->
<*> Ext3 journaling file system support
[*] Ext3 extended attributes
Creating and running multiple Linux distributions inside a Linux host system was not enough, however. I needed to reach them on behalf of a true network connection. (It is no accident that UML is good for network simulation and study; it can be configured and interact like a network in several ways.) I used the Universal TUN/TAP device driver, described as follows in file tuntap.txt in the Linux kernel tree:
"It provides packet reception and transmission for user space programs. It can be seen as a simple Point-to-Point or Ethernet device, which, instead of receiving packets from physical media, receives them from user space program and instead of sending packets via physical media writes them to the user space program."
TUN/TAP support makes it possible to establish virtual network interfaces. TUN or TAP interfaces behave as an IP or Ethernet network interface or as a character device, respectively. In my case, I created a TAP instance on the host machine for Ethernet frames exchange, talking with the guest Ethernet network interface on one side and with the host kernel on the other. In this way, the guest system sees an eth0 device because it had a real Ethernet card (i.e., its eth0 device file will talk with the host Linux system TAP device).
Of course I need TUN/TAP support on both host and guest Linux kernels:
Device Drivers --->
[*] Network device support --->
<M> Universal TUN/TAP device driver support
Next, I had to decide if I wanted the host to act as a switch connecting different network segments on the basis of TCP addresses or on the basis of MAC addresses. In the former case, the host becomes a router (Layer 3 switch), in the latter, an Ethernet bridge (Layer 2 switch). I chose the latter solution, assuming that I had additional available IP addresses for my Ethernet LAN. I also assumed that my LAN had a machine acting as a router to exit on to the Internet. Of course, the kernel host machine had to support Ethernet bridging:
Networking options --->
<M> 802.1d Ethernet Bridging
Moreover, I needed the bridge-util package installed on the host machine:
apt-get install bridge-util