April 13, 2004 Edition

By Adam Israel, Amit Gurdasani, Kirk Ruff, Matt Andrews


udev and automatic network configuration

This week, Linux.ars takes a look at udev, the user-space implementation of devfs, as well as a guide to automatic network configuration with Debian. Last, but not least, we take a peek at watching cable with tvtime.


udev: automatic device node creation

Device nodes

Device nodes in *nix systems are special files that provide a sort of interface to programs in order to facilitate the manipulation of devices both physical devices as managed by device drivers in the kernel and virtual devices that control various aspects of the kernel's behavior. Whenever you hear the phrase "everything is a file to UNIX," the speaker is referring in part to the concept of device nodes, which can frequently be accessed and handled like any other file in the system and for special manipulation (such as changing certain device settings), the ioctl system call can be made on these device nodes.

Traditionally, device nodes are stored in /dev; under Linux, the naming scheme and hierarchy of device nodes is specified by the Linux Standard Base. Currently, the device driver in the kernel expects to have data and control messages "addressed" to a pair of numbers, called the major and minor device numbers that the corresponding special files hold.

As an example, the input core mouse device that is used to get readings off a USB mouse is a character special file (which is to say that a program can read a stream of characters from it, as opposed to a block special file, which supports the ability to consistently seek into arbitrary locations within the device; examples are storage volumes, such as IDE disks and partitions). The input core mouse device carries the major number 13 and minor number 63. (A specific pair of major and minor numbers will indicate different block and character devices; for instance, while the character device with numbers 13, 63 is the input core mouse driver, the block device with those major and minor numbers is the sixty-third partition on an XT hard disk. In other words, major and minor numbers live in different namespaces for character and block devices.)

A device node is created using the mknod command:

root@athena:~# mknod sda b 8 0
root@athena:~# mknod mice c 13 63

For security reasons, only the superuser (or perhaps a user with the appropriate capabilities granted) can create device special files.

The assignment of major and minor numbers is managed by the jocularly-named Linux Assigned Names and Numbers Authority (LANANA).


The past: devfs

While static device nodes work well, there are good reasons to go for dynamically-created device nodes based upon the devices in the system and device drivers present in the kernel.

For one thing, device nodes take up a lot of filesystem inodes. There is typically one or more inode for every file present on a filesystem. Inodes are a form of metadata used by the filesystem driver to keep track of things such as the ownership and permissions on a file, the date and time when it was created or modified, the date and time of the last access, as well as the date and time of inode modification. For special files, the inodes also carry the kind of file (whether it is a plain file, a directory, a first-in-first-out pipe, a socket file, a block or character device file, etc.). They also carry information such as the major and minor numbers. There is a limited number of inodes on a filesystem, usually determined when the filesystem is created. Inodes themselves take up a significant amount of space, so that where disk space is at a premium (for instance, on a floppy disk), the number of inodes available can quickly become a limitation. For this purpose, it helps to have only those device nodes that are useful; that is, those to which there are corresponding devices in the system.

There is such a large variety of devices available for use in a Linux system that it becomes highly impractical to make device nodes for each and every device available under /dev in every single system. Also, with an increasingly-diverse variety of removable devices available, the device configuration of a Linux system can be expected to change continually. Thus it becomes difficult for the subset of device nodes provided during installation to cover every device that is ever attached to the system.

In such circumstances, it is advantageous for the system to generate device nodes itself in response to devices being attached to the system and drivers being loaded dynamically.

Many commercial UNIXes have the ability to generate device nodes under /dev depending on the physical devices and configuration of the system on the fly. For instance, the device nodes corresponding to each volume (single SCSI or IDE disks as well as RAID arrays and logical volumes on such disks or arrays, along with devices for individual partitions or slices) are generated automatically, as well as device nodes for the frame buffers used for the graphical display, the virtual and physical terminal devices upon which consoles and terminal emulation such as that provided by xterm are based, device nodes for the input devices, etc.

Linux gained the ability to generate dynamic device nodes in kernel 2.4 with the devfs filesystem driver. devfs, the device filesystem, is a virtual filesystem that generates a device node for each device driver that is loaded and provides a devfs name, so that devfs knows what to call the device. It also has some interesting features, such as the ability to load the appropriate device driver when a program attempts to access a nonexistent device node. It has a unique naming scheme organized as hierarchies of directories under /dev, so that (for instance) SCSI bus device nodes can be found under /dev/scsi, organized by the number assigned to the host bus adapter during device enumeration, the LUN at which the device is attached, the volume number, etc.

devfs, unfortunately, while very ambitious in scope, had a number of disadvantages. For instance, not long after its incorporation into the Linux 2.4 kernel, its maintainer appeared to have abandoned it. There are a number of concurrency control issues in the devfs code that cause it to behave erroneously on multi-processor system. More importantly, devfs enforced a naming policy in violation of the Linux Standard Base, though a user-space daemon called devfsd was written to maintain symbolic links named according to the LSB specification to the devfs device node. Many developers did not like the fact that devfs moved naming policy into the kernel in fact, into every driver that wanted to have devfs create device nodes for it. Most importantly, the scheme by which devices were named according to the order devices were enumerated prevented devices from having consistent names when they were moved around, since that would change the order in which they were enumerated. This would mean, for instance, that if the third IDE disk was moved, it might be enumerated as the second disk at next boot, named as the second disk and treated (incorrectly) as the second disk by programs that might expect the disk to have different layout and data on it.

It was clear to kernel developers that devfs was inadequate, and that another solution was needed.

Enter udev

Greg Kroah-Hartman of IBM's Linux Technology Center decided to create an alternative to devfs that ran in user-space (that is, not in the kernel, but as just another daemon/service program). He christened it udev, for "user-space implementation of devfs".

udev works by taking information from two sources, the sysfs filesystem that carries information about devices published by drivers in the /sys hierarchy, as well as the hotplug framework that monitors busses that support hotplugging, such as USB, SCSI and IEEE 1394/FireWire. /sys carries a directory hierarchy organized by bus and device position that carries information about devices detected by the kernel as well as the resources allocated to them (some of this information was formerly in the /proc hierarchy). This information includes such things as unique device identifiers encoded into ROM chips present in physical devices in order to be able to identify them uniquely.

The hotplug framework obtains notification from the kernel whenever a new device is attached to the system or removed from the system, identifies the kind of device involved, and runs commands and scripts to handle the changes. udev provides a program (udevsend) that is run whenever such a change occurs and notifies the event serializer, udevd. The latter then runs udev in the correct order in order to create or remove device nodes. udev uses unique serial numbers and other such information from sysfs to create persistent names for device nodes. It can also invoke udev_dbus to send information about device names and hot-plugged devices via D-BUS (along with which it forms part of Project Utopia). Programs that subscribe to such notifications on D-BUS (such as future volume management frameworks in desktop environments such as KDE and GNOME) can then present an appropriate interface to the user.

In order to name devices using the unique identifiers gleaned from sysfs, udev uses its namedev framework to handle naming policy. This primarily consists of scripts and files typically found under /etc/udev/udev.rules.d that govern what names should be given to what sorts of devices. The policy encoded in these files is very customizable and the default is a good implementation of the LSB rules. The names provided corresponding to the unique identifiers are stored in a database, typically called /dev/.udev.tdb, that persists across reboots and provides udev the ability to give persistent, consistent names to devices, no matter in which order they are enumerated.

Future directions

Distributions are starting to incorporate udev in their default installations. This frequently involves providing a small number of static nodes in /dev in order to allow the system to boot to the point where udev can be started in order to manage the device nodes. Packages provided by certain distributions (such as Debian unstable) are able to mount a tmpfs (virtual memory-backed filesystem) over /dev and run udev to manage nodes on top of it. In the future, udev is intended to be incorporated into init, the first program run by the kernel when the system is booted, in order to obviate the need for even a few static nodes on the disk under /dev.

In fact, for kernel 2.7/2.8, it is tentatively planned that the device "addresses" the major and minor numbers encoded in the device nodes shall themselves be made dynamic, removing the need for LANANA or any sort of assignment scheme. The kernel may even assign major and minor numbers randomly, as long as they stay unique during a particular session. This information will be picked up by udev from sysfs in order to create the corresponding device nodes. The programs that use these device nodes by name will never need to know that the major and minor numbers are temporary; as long as the device node names are persistent, everything will run smoothly.

With the wide adoption of udev, D-BUS and related projects such as HAL and the GNOME volume manager, the biggest win will be for desktop users, since the desktop environment will finally be well-integrated with the rest of the platform, giving users easy ways to manage their devices and storage volumes using well-integrated graphical user interfaces.


Tools, Tips, and Tweaks: guessnet, ifplugd, and waproamd

Wouldn't it be nice just to pick up your laptop, plug it in at work, and be connected to the network without having to do anything other than connect the cable? Or to take a WiFi laptop to school, have it recognize the hotspot there and set up the appropriate WEP key*? Well, with the help of a couple of programs and some configuring this can become a reality.

What exactly are we doing? It's actually quite simple. We are going to set up multiple logical interfaces for a single physical interface. Next, we are going to help the computer recognize what network it is on and bring up the appropriate logical interface. Finally, we will show the computer how to recognize that it is connected to a network and have it trigger the network recognition

Before we begin

A few quick things things to note. First, this assumes that you have root access on the machine you are using. Second, this was written with Debian systems in mind, so theoretically, this should work with any Debian-based distribution. Things are likely very different in other distributions, especially Red Hat-based distributions. Distributions such as Knoppix, Morphix, Xandros and Lindows may be similarly configured. A guide to doing something similar in Red Hat is in the works. Finally, be sure to make a back up before you make any changes. If something bad occurs a backup will allow you to restore your network settings, which is better than no networking at all.

First things first . /etc/network/interfaces

If you know what's in /etc/network/interfaces already and know how to use ifup and ifdown, you can skip this section. Otherwise, read on.


/etc/network/interfaces is where the configuration of your network is stored. A basic interfaces file might look like this:

# /etc/network/interfaces -- configuration file for ifup(8), ifdown(8)
# This is a comment
# Set up the looback (lo) interface
iface lo inet loopback
# Use dhcp to configure eth0
iface eth0 inet dhcp
# Use a static configuration with eth1
iface eth1 inet static
# Automatically bring up lo and eth0
auto eth0 lo

What do all those lines mean?

how to use the information in /etc/network/interfaces

In order to make use of the information in this file, you use the ifup and ifdown commands. To use the second card and not the first card you would use the following commands (you will probably need root access to execute them):

ifup eth1
ifdown eth0

Using multiple configurations for an single interface

Okay, that's great. But what if you want to have a different configuration for work and home and you only have one card? The interfaces file can handle that, too. You don't have to name the logical interface based on the physical interface. For example,

# Use dhcp to configure work
iface work inet dhcp
# Use a static configuration at home
iface home inet static

The only problem is that ifup doesn't know what to do with those configs. When you want ifup now, you need to tell it what config to use. All you do is use

ifup =

An example:

ifup eth0=work
ifdown eth0
ifup eth0=home

Notice that you don't have to tell ifdown which config to use; it already knows which is being used.

Discovering which configuration to use . guessnet

All that is well and good, but we don't want to have to tell the computer where we are. This is step one of automatic configuration. The interfaces file allows you to do a couple of neat things. There is a special stanza that allows you to plug in a script to determine where you are. This stanza is the mapping stanza. First, we add the following lines:

auto eth0
mapping eth0
iface home inet dhcp
iface work inet dhcp

The script stanza specifies what command/script to run. It is passed the interface name (eth0) in this case. It is expected to echo a logical interface name (home or work). Basically the mapping command will run the script and the run ifup eth0=