[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

DHCP configured NFS root using initrd-tools



Just over a year ago (4 Sep 2001), Russell Coker dropped a note in here
re. getting an initrd NFS root boot working using initrd-tools's mkinitrd. 

I've developed a slightly different version which includes DHCP support,
rather than a hardcoded network config. I've also used modprobe to 
figure out the module dependencies. The new mkinitrd script is below,
if anyone's interested.

The DHCP config should specify the 'next-server' and 'filename' parameters 
to the client for the NFS server and export, e.g. for dhcp3:

    next-server preston;
    filename "/netboot/root/gromit";

(Note udhcpc doesn't ask for the root-path option, which is why I've 
"overloaded" the filename parameter).

Other details of the netboot are as per Russell's post (though I use PXE 
& etherboot to fetch + load the nbi image, works well).



-- 
Ben Low
"A man can't be too careful in the choice of his enemies."
	- Oscar Wilde (1854-1900)

/etc/mkinitrd/scripts/nfsroot:
#!/bin/sh
# mkinitrd initrd-module-and-script-generator
# - copies required modules and creates a set of initrd scripts so as 
#   to mount an nfs root when booting a diskless box
# i.e. this script is run when *generating* the initrd image, 
# and is meant to figure out what modules need to be included
# in the initrd image; it further generates script(s) to be run by the 
# initrd's init to mount the nfs root at boot time
#
# env vars set by mkinitrd:
# INITRDDIR - initrd assembly area (e.g. /tmp/mkinitrd.4553/initrd)
# MODULEDIR - module source directory (e.g. /lib/modules/2.4.18)
#
# works with initrd-tools (0.1.33), udhcpc (0.9.7-4)
# 
# BDL Jul02
# Changelog:
# - removed pivot_root hack, now done by mkinitrd's init (initrd-tools 0.1.26)

# initrd required programs:
# mkinitrd conveniently handles including the required binaries
# (and supporting libraries) listed in /etc/mkinitrd/exe
# - i.e. make sure mount, pivot_root etc are listed there
# - inbuilt exe list is: /bin/sleep /sbin/modprobe /sbin/insmod /bin/ash
#    (ref. /usr/sbin/mkinitrd)
#
# required modules:
# mkinitrd can probe to determine which modules need to be 
# included in the initrd image for local devices (eg ide-disk),
# but can't grok nfs
# - need to manually copy them into the initrd image (e.g below)
# - or, just say "MODULES=all" in mkinitrd.conf (at 100Mb/s, what's an 
#   extra MB or two? :-)
#

#################################
# required modules
# - dependancies are automatically determined (below)

## mkinitrd.conf MODULES=all
#MODULES=

# only include those which are going to be loaded
MODULES=`grep -v '^#' /etc/mkinitrd/modules`

#################################

if ! [ "$INITRDDIR" -a "$MODULEDIR" ]; then
	echo "usage: INITRDDIR=<initrd dir> MODULEDIR=<module dir> $0"
	exit 1
fi

############################################################################
# 1. create various mount points (in initrd filesystem)

# create var ramfs mount point (mainly for dhcp params)
# - initrd fs will generally be cramfs, which is ro (should be called cromfs!)
[ -d "$INITRDDIR/var" ] || mkdir $INITRDDIR/var

# create nfs mount point in initrd fs
[ -d "$INITRDDIR/mnt" ] || mkdir $INITRDDIR/mnt

# hey, can't hurt... (right? :-)
[ -d "$INITRDDIR/proc" ] || mkdir $INITRDDIR/proc


############################################################################
# 2. copy required modules
# a simple modprobe -v -n <module> does pretty much what we want 
# (lists modules which would be loaded, i.e. the dependancies, of 
# <module>), though we need to tell it to look in the new moduledir, 
# not the running kernel's
 
modulesconf=`mktemp -p $INITRDDIR/..` || exit 1

# fabricate a bare modules.conf for the target module tree
# (basically a stripped down /etc/modules.conf; ref. modules.conf(5))
# - must quote special chars (e.g. ` => \')
cat > "$modulesconf" <<-EOF
	depfile=$MODULEDIR/modules.dep
	generic_stringfile=$MODULEDIR/modules.generic_string
	pcimapfile=$MODULEDIR/modules.pcimap
	isapnpmapfile=$MODULEDIR/modules.isapnpmap
	usbmapfile=$MODULEDIR/modules.usbmap
	parportmapfile=$MODULEDIR/modules.parportmap
	ieee1394mapfile=$MODULEDIR/modules.ieee1394map
	pnpbiosmapfile=$MODULEDIR/modules.pnpbiosmap

	path[toplevel]=$MODULEDIR
EOF

# determine full paths of desired modules and cp into INITRDDIR
# - cpio invocation is as per mkinitrd
(
for m in $MODULES; do
	# look for "insmod <module>", one for each dependancy + the target
	modprobe -C "$modulesconf" -v -n $m | awk '$1 ~ /insmod/ { print $2 }'

	# alas, in '-n' mode, modprobe doesn't return correct exit status
	if [ $? -ne 0 ]; then
		echo "modprobe failed, aborting" >&2
		exit 1
	fi
done
) | cpio -pLd --quiet $INITRDDIR/

rm "$modulesconf"


############################################################################
# 3. create initrd config scripts:
#    i) configure networking and fetch nfs params via dhcp client
#   ii) mount nfs fs
#
# The mkinitrd init runs the files in /scripts/* (sourcing .sh files in a 
# subshell).
# These scripts may in turn call others, e.g. the DHCP client script.
# - note that due to the initrd fs being read-only, it's difficult to pass 
#   parameters back to the caller (i.e. by writing a state file to be 
#   sourced by others)
#   - thus, a writeable tmp filesystem is required
#
#  i) the DHCP client (scripts/40networking) calls /etc/udhcpc-script when 
#     a lease is acquired (or not acquired - ref udhcpc(8))
#     - this client script gets passed the DHCP parameters as env variables
#       - the nfs-network is configured
# ii) 45mountnfs 
#     - uses the DHCP-supplied 'filename' as the nfs export to use as /
#


########################################
# dhcp guff

UDHCPCSCRIPT=/etc/udhcpc-script
UDHCPENV=/var/dhcp.env
cat > $INITRDDIR/$UDHCPCSCRIPT <<EOF
#!/bin/sh
# udhcpc client script
# - udhcp will have set various env variables according to the new state of
#   the interface
# - NOTE: udhcpc doesn't actually configure the interface
# - NOTE: some options are very similar (cf. bootfile, boot_file !)
#   - udhcpc only requests a select few options: option-{1,3,6,12,51,53,54}
#     = {subnet mask, router, dns, hostname, dhcp msg type, serverid, lease t.}
#       see http://www.freesoft.org/CIE/RFC/2131/8.htm for bootp/dhcp format
#   - ISC DHCP options => udhcp script env variables:
#     - filename => boot_file
#     - dhcp-server-identifier => serverid (i.e. DHCP server address)
#     - next-server => siaddr (NOTE: v0.9.6 has a bug siaddr==yiaddr)
#

case "\$1" in

	deconfig)	echo "\$0: \$interface: deconfig"

				# i/f up, but deconfigured
				ifconfig "\$interface" 0.0.0.0
	;;

	bound)		echo "\$0: \$interface: bound to \$ip"

				ifconfig "\$interface" "\$ip" netmask "\$subnet" \
					\${broadcast:+broadcast \$broadcast}

				route add default gw "\$router"

				# set hostname, may be echoed back to the server in the
				# client's dhclient script (so as to get added to lease file)
				[ "\$hostname" ] && hostname "\$hostname"

				# udhcp 0.9.6 bug: siaddr incorrectly == yiaddr
				siaddr="\$serverid"

				# ok, write env to file for others to source...
				set > $UDHCPENV
	;;
	
	renew)		echo "\$0: \$interface: renew \$ip";
	;;

	nak)		echo "\$0: \$interface: nak: \$message"
	;;

	*)			echo "\$0: huh? unknown udhcpc action: [\$1]"
	;;
esac

EOF

chmod 755 $INITRDDIR/$UDHCPCSCRIPT



########################################
# scripts/


cat > $INITRDDIR/scripts/05mounts <<'EOF'
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e
[ -d /proc/1 ] || mount -n -t proc proc /proc
mount -n -t ramfs none /var
trap - 0
EOF
chmod 755 $INITRDDIR/scripts/05mounts


# note: heredoc not quoted-literal, watch interpolation
cat > $INITRDDIR/scripts/40networking <<EOF
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e

ifconfig lo 127.0.0.1 netmask 255.0.0.0

# udhcpc: lightweight dhcp client
# -f = foreground (don't fork)
# -n = "now" (exit if lease cannot be obtained)
# -q = quit after (not) obtaining lease
# -s = script to run on dhcp events (e.g. getting a lease)
# UDHCPCSCRIPT manages the interface config and writes the DHCP parameters
# (options) to UDHCPENV, as shell environment variables
udhcpc --foreground --now --quit --script=$UDHCPCSCRIPT

trap - 0
EOF
chmod 755 $INITRDDIR/scripts/40networking


# note: heredoc not quoted-literal, watch interpolation
cat > $INITRDDIR/scripts/45mountnfs <<EOF
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e

. $UDHCPENV


# could/should mount w/ 'lock'?
mount -n -o nolock,rw,rsize=8192,wsize=8192 "\$siaddr:\$boot_file" /mnt

trap - 0
EOF
chmod 755 $INITRDDIR/scripts/45mountnfs


# leave things as tidy as when we arrived :-)

cat > $INITRDDIR/scripts/S99unmounts <<'EOF'
#!/bin/sh
trap '/bin/echo "FAILED [$0]"; /bin/sh' 0
set -e
umount -n /var
umount -n /proc
trap - 0
EOF
chmod 755 $INITRDDIR/scripts/S99unmounts



Reply to: