9.2 Server Setup for Network Boot

As we saw in Chapter 2, setting up a target for network boot is ideal during the early stages of development, because you can gradually modify the kernel and the root filesystem without having to update the target's storage devices every time you make a modification. Though not all bootloaders can use this setup to boot, I recommend that you use such a setup whenever possible.

As I said earlier, the simplest way to boot your target from the network is to use BOOTP/DHCP, TFTP, and NFS. BOOTP/DHCP is the standard way to provide a network host with basic boot information, including the location of other servers such as TFTP and NFS. TFTP is the simplest network protocol for downloading remote files. In the case of an embedded Linux system, it is used by the target to obtain a kernel image from the TFTP server. Finally, NFS is the standard and simplest protocol for sharing entire directory trees between a client and a server. In the case of an embedded Linux system, it is used by the target to mount its root filesystem from the NFS server. NFS cannot be used for any earlier activity, because it requires a booted Linux kernel to operate. Together, these three protocols provide a very efficient host/target development setup.

To enable network booting of the target, you must set up the development host's network services so that the target can access the components it needs. In particular, you need to set up a host to respond to BOOTP/DHCP requests, provide a kernel using a TFTP server, and enable NFS mounts. The subsections below discuss each issue separately.

9.2.1 Setting Up the DHCP Daemon

Unlike other network services, DHCP is not dependent on the internet super-server. Instead, the DHCP daemon is a service of its own, and you need to start it manually. First, you need to make sure that the DHCP server is installed on your system. Though you can download it from http://www.isc.org/, the DHCP server is part of most mainstream distributions.

If you are using an RPM-based distribution, use the following command to check for the presence of the DHCP daemon:

$ rpm -q dhcp
dhcp-2.0-5

In this case, DHCP 2.0-5 is already installed. If it is not already installed on your system, use the appropriate tools for your distribution to install the DHCP server. Note that most distributions include two DHCP packages, a client and a server. The package containing the client is usually called dhcpc-VERSION. There is an additional "c" after "dhcp" to identify the client package.

To operate properly, the kernel on which the DHCP server runs has to be configured with the CONFIG_PACKET and CONFIG_FILTER options. The kernels shipped by default in most distributions almost always have these enabled. If you are in the habit of building your own kernels for your workstation, as I often do, watch out for those options when configuring the kernel. If the kernel wasn't built properly, the DHCP daemon will output the following message when it tries to start:

socket: Protocol not available - make sure CONFIG_PACKET and CONFIG_FILTER are 
defined in your kernel configuration!
exiting.

With the package installed and the kernel properly configured, create or edit the /etc/dhcpd.conf file and add an entry for your target. For example, here is the /etc/dhcpd.conf file for my control module:

subnet 192.168.172.0 netmask 255.255.255.0 {
        option routers 192.168.172.50;
        option subnet-mask 255.255.255.0;

        host ctrl-mod {
                hardware ethernet 00:D0:93:00:05:E3;
                fixed-address 192.168.172.10;
                option host-name "ctrl-mod";
                next-server 192.168.172.50;
                filename "/home/karim/vmlinux-2.4.18.img";
                option root-path "/home/karim/ctrl-rootfs";
        }
}

Essentially, this entry states that the host and target are on the 192.168.172.0 network, that the TFTP server is located at 192.168.172.50, and that the address allocated to the target when it issues its DHCP or BOOTP request is 192.168.172.10. The hardware ethernet field uniquely identifies the target through its MAC address, which is 00:D0:93:00:05:E3 for my control module. The fixed-address field tells the DHCP server which IP address should be allocated to the designated MAC address. The option host-name field gives the hostname to the target so that it can use it internally. The next-sever tells the target where the TFTP server is located. The filename field is the filename[4] of the image that has to be loaded by the target. According to RFC 2131, which specifies DHCP, the filename is limited to 128 bytes. Finally, the option root-path field provides the path to the target's root filesystem on the NFS server. If your target does not need to load its root filesystem from an NFS server, you can omit this last field. Because the host is the only network link to the target in this case, option routers points to the host's address. If the target was linked to an entire network with a real router, option routers should point to that network's default router.

[4] For the example to fit in the printed page's width, I avoid using the complete /home/karim/control-project/control-module/... path. Use the actual complete path for your own development.

The example configuration provided above should be easy to adapt to your own target. If you need more information regarding the configuration of the DHCP server, have a look at the manpage for dhcpd.conf and the sample configuration file installed by your distribution, if one is present.

Note that if you are using a version of the DHCP daemon later than 3.0b2pl11, such as the one shipped with Red Hat 8.0, you will need to add the following line to your dhcpd.conf file:

ddns-update-style ad-hoc;

With the DHCP server configured for the target, you are almost ready to start the DHCP server. Before you do so, however, you need to make sure the /var/state/dhcp/dhcpd.leases file exists. If it doesn't, create it using the touch command. If the file isn't created, the DHCP daemon will refuse to start.

Finally, start the DHCP server. On distributions based on Red Hat, enter:

# /etc/init.d/dhcpd start

9.2.2 Setting Up the TFTP Daemon

The first step in setting up the TFTP daemon is to make sure the TFTP package is installed. Though the latest version of the TFTP daemon is available for download as part of the NetKit package at ftp://ftp.uk.linux.org/pub/linux/Networking/netkit/, TFTP was most likely already installed on your system as part of your distribution or is available to be installed from your distribution's CDs.

If you are using an RPM-based distribution, use the following command to check for the presence of the TFTP daemon:

$ rpm -q tftp
tftp-0.16-5

In this case, TFTP 0.16-5 is already installed. If it is not available on your system, install the TFTP package using the appropriate tool for your distribution. Alternatively, if your system doesn't rely on a package manager or if some components have been installed without a package manager, you can also check for the presence of the actual TFTP daemon binary using the whereis command.

Once the package is installed, enable the TFTP service by modifying the appropriate internet super-server configuration file. In brief, the internet super-server listens on designated ports on behalf of the various network services. When a request for certain service is received, the super-server spawns the appropriate daemon and hands it the request. Hence, only the minimal number of daemons run at all times. TFTP is one of the daemons normally handled by the super-server.

To enable the TFTP service in a system based on the inetd super-server, edit /etc/inetd.conf, uncomment the line for the TFTP service by removing the # character at the beginning, and send a SIGHUP signal to the inetd process so that it rereads its configuration file. To enable the TFTP service in a system based on the xinetd super-server, edit /etc/xinetd.d/tftp and comment the line containing disable = yes by adding a # character at the beginning. As with inetd, you must send a SIGHUP to xinetd.

Finally, you must provide the TFTP server with a list of directories containing files that should be made available to TFTP clients. In a system based on the inetd super-server, append the list of directories to the TFTP line in /etc/inetd.conf. In a system based on the xinetd super-server, edit the /etc/xinetd.d/tftp file and append the list of directories to the server_args = line. The default directory for TFTP is /tftpboot. You may choose to modify this to match your setup. Whichever directory you choose, make sure its access permissions include read and execute for the "other" permission.

For example, here is a TFTP line in /etc/inetd.conf for a host using the inetd super-server:

tftp   dgram  udp    wait   root  /usr/sbin/tcpd  in.tftpd /home/karim/

In this case, images are placed in the /home/karim directory, which has the following permissions:

$ ls -ld /home/karim
drwxr-xr-x    4 karim     karim         4096 Aug 29 16:13 karim

Here is a modified /etc/xinetd.d/tftp file from a Red Hat-based installation providing the same functionality for a host using the xinetd super-server:

service tftp
{
        socket_type             = dgram
        protocol                = udp
        wait                    = yes
        user                    = root
        server                  = /usr/sbin/in.tftpd
        server_args             = /home/karim
#        disable                 = yes
        per_source              = 11
        cps                     = 100 2
}

Regardless of the super-server in use on a host, the TFTP service is usually disabled by default. Hence, even if you use the default /tftpboot, you will need to modify the super-server's configuration files to enable TFTP.

9.2.3 Mounting a Root Filesystem on an NFS Server

As I explained in Chapter 2, while a bootloader and kernel must be stored locally or retrieved to local storage through one of the methods shown earlier, the target's kernel can mount its root filesystem from a remote NFS server. To this end, the NFS server must be properly installed and configured. Chapter 6 showed how to build your target's root filesystem. Though Chapter 8 showed how to prepare this filesystem for use in the target, the root filesystem we created in Chapter 6 does not need any special preparation for use by the NFS server.

The NFS server daemon is available in two flavors: as a standalone user application or as a part of the kernel. Besides being faster, the latter is also the standard way most distributions are configured. In addition to the NFS server itself, you need to have the NFS utilities installed. Usually, there is an nfs-utils package as part of your distribution. Use the following command to identify whether nfs-utils is installed:

$ rpm -q nfs-utils
nfs-utils-0.3.1-13

With the nfs-utils installed, you need to make sure that the appropriate configuration files are present and that the corresponding services are started.

The main file we need to configure for the NFS server is /etc/exports. Entries in this file describe the directories each host or set of hosts can access. As an example, here is the entry in my /etc/exports for my control module:

/home/karim/ctrl-rootfs 192.168.172.10(rw,no_root_squash)

This entry states that the machine with address 192.168.172.10 has read and write (rw) access to the /home/karim/ctrl-rootfs directory, which is the path to the root filesystem we built for the target in Chapter 6. In addition, the no_root_squash argument indicates that the server should allow the remote system to access the directory with its root privileges. These are very powerful rights that we are granting to the target. If we have total control over access to the device, as is the case in most development setups, there is obviously no security risk. If, however, the target's location is less secure or if it is directly connected to the Internet, for example, you may prefer to use the default root_squash instead. In that case, the target will not be able to write to most of its own root filesystem, though it will still be able to read and write to all directories and files that are readable and writable by anybody. In practical terms, however, the target's operation will be very limited.

Because offering the NFS service also involves the risk of network abuse, it is often pertinent to use some minimal protection mechanisms to avoid intrusions. One simple way to do this is to customize the /etc/hosts.deny and /etc/hosts.allow files to restrict access to network services. For example, here is the /etc/hosts.deny file for my Red Hat-based host:

#
# hosts.deny
#

portmap: ALL
lockd: ALL
mountd: ALL
rquotad: ALL
statd: ALL

and here is my /etc/hosts.allow file:

#
# hosts.allow
#
 
portmap: 192.168.172.10
lockd: 192.168.172.10
mountd: 192.168.172.10
rquotad: 192.168.172.10
statd: 192.168.172.10

The rules specified in this files restrict access to the various file-sharing services. Together, these files indicate that only the machine with address 192.168.172.10 can use the NFS services. This is fine in the case of my setup, since I don't want to share my workstation with anyone else. Even if you do not customize /etc/hosts.deny and /etc/hosts.allow, I encourage you to take security issues to heart and use whichever means necessary, such as backups, to protect your work.

Once the configuration files are created, you can start the portmapper service, which is required by the NFS server:

# /etc/init.d/portmap start

Finally, you can start the NFS server itself:

# /etc/init.d/nfs start

If you would like more information on the configuration of remote boot using NFS, see the two Diskless root NFS HOWTOs on the issue at the LDP. Also, you may be interested by the NFS HOWTO, also at the LDP.