Replacing KLIBCs ipconfig with UDHCPC inside live initramfs tools
Stephan Adig
sh at sourcecode.de
Tue Jan 11 13:07:25 CET 2011
Dear Colleagues,
during our work here at our office, we were trying to deploy newly
bought HP BL465C G7 Bladeserver
(http://h10010.www1.hp.com/wwpc/us/en/sm/WF05a/3709945-3709945-3328410-241641-3328419-4132949.html) with 10GbE NC551i FlexFabric 2 Ports NICs.
Those NICs are represented by the be2net and be2iscsi kernel drivers.
And then came the problem...strangely klibcs ipconfig wasn't able to
reliable do dhcp requests inside the initramfs.
As the first PXE DHCP/BOOTP request was working and we are using the
BOOTIF parameter of pxelinux to catch exactly that interface, we were
sure that our DHCP infrastructure is working (we are doing some dhcp
relay configs inside our network).
So, during the last days I was debugging and debugging without finding
any hint why ipconfig is not able to accept the dhcp provided IP
address, while we saw the request on the dhcp server.
Today I was so frustrated that I thought to myself "when ipconfig is not
working, I need some better dhcpclient".
I remembered the LTSP project, because they fought with the very same
problem some time ago, and checked their repositories for some initramfs
udhcpc magic and found it.
http://bazaar.launchpad.net/~ltsp-upstream/ltsp/ltsp-trunk/annotate/head:/client/initramfs/hooks/udhcp
this is the hook script for copying the udhcpc binary into the
initramfs.
http://bazaar.launchpad.net/~ltsp-upstream/ltsp/ltsp-trunk/annotate/head:/client/initramfs/scripts/init-premount/udhcp
This is the init-premount script from them.
As we are not using the standard init stuff from initramfs but the live
overlay of it I had to adjust the script to my needs.
As explained above, we are using the BOOTIF parameter from pxelinux.0
therefore I didn't play around with all the other ip stuff and came
directly to the point.
chroot <FAIDIR>/nfsroot/live/filesystem.dir
and changed the do_netmount shell function
in /usr/share/initramfs-tools/scripts/live:
The function now looks like this:
do_netmount ()
{
rc=1
modprobe -q af_packet # For DHCP
if [ -x /sbin/udevadm ]
then
# lenny
udevadm trigger
udevadm settle
else
# etch
udevtrigger
udevsettle
fi
# ipconfig ${DEVICE} | tee /netboot.config
if [ -n "${BOOTIF}" ]; then
# pxelinux sets BOOTIF to a value based on the mac
address of the
# network card used to PXE boot, so use this value for
DEVICE rather
# than a hard-coded device name from initramfs.conf.
this facilitates
# network booting when machines may have multiple
network cards.
# pxelinux sets BOOTIF to 01-$mac_address
# strip off the leading "01-", which isn't part of the
mac
# address
echo "BootIF ${BOOTIF}"
temp_mac=${BOOTIF#*-}
# convert to typical mac address format by replacing "-"
with ":"
bootif_mac=""
IFS='-'
for x in $temp_mac ; do
if [ -z "$bootif_mac" ]; then
bootif_mac="$x"
else
bootif_mac="$bootif_mac:$x"
fi
done
unset IFS
# look for devices with matching mac address, and set
DEVICE to
# appropriate value if match is found.
for device in /sys/class/net/* ; do
if [ -f "$device/address" ]; then
current_mac=$(cat "$device/address")
if [ "$bootif_mac" = "$current_mac" ];
then
DEVICE=${device##*/}
echo "DEVICE FOUND: ${DEVICE}"
break
fi
fi
done
fi
##################################################
#
# generate /tmp/dhcp-script.sh
#
##################################################
echo '#!/bin/sh
[ "$1" = "bound" ] || exit
echo "# this file contains....." > /tmp/dhcp-info.conf
for var in interface ip siaddr sname boot_file subnet timezone router \
timesvr namesvr dns logsvr cookiesvr lprsvr hostname bootsize domain
\
swapsvr rootpath ipttl mtu broadcast ntpsrv wins lease dhcptype
serverid \
message tftp bootfile; do
eval value=\"\$$var\"
if [ -n "$value" ]; then
echo $var="\"$value\"" >> /tmp/dhcp-info.conf
fi
done' > /tmp/dhcp-script.sh
chmod +x /tmp/dhcp-script.sh
ip link set $DEVICE up
echo "Sleeping 5 seconds to reall have an up interface"
sleep 5
#####################################
#
# Do UDHCPC
#
#####################################
while [ -z "$configured" ]; do
if udhcpc -n -s /tmp/dhcp-script.sh -i $DEVICE
> /dev/null 2>&1; then
configured="true"
break
fi
done
####################################
#
# source in dhcp-info.conf
#
###################################
. /tmp/dhcp-info.conf
ip address add $ip/$(subnet_to_cidr $subnet) broadcast
${broadcast:-+} dev $DEVICE
for i in $router
do
ip route add default via $i dev $DEVICE
done
echo "$DEVICE configuret at $ip:$tftp:$router:$subnet:$hostname"
read dns0 dns1 rest_dns <<EOF
$dns
EOF
read router0 rest_routers <<EOF
$router
EOF
echo "DEVICE='$DEVICE'
IPV4ADDR='$ip'
IPV4BROADCAST='$broadcast'
IPV4NETMASK='$subnet'
IPV4GATEWAY='$router0'
IPV4DNS0='$dns0'
IPV4DNS1='$dns1'
HOSTNAME='$hostname'
DNSDOMAIN='$domain'
NISDOMAIN='$domain'
ROOTSERVER='$siaddr'
ROOTPATH='$rootpath'
filename='$bootfile'
DNS_SERVER='$dns'
SEARCH_DOMAIN='$domain'
NTPSVR='$ntpsrv'
TIMESVR='$timesvr'
TIMEZONE='$timezone'
LOGSVR='$logsvr'" > /tmp/net-$DEVICE.conf
# source relevant ipconfig output
OLDHOSTNAME=${HOSTNAME}
. /tmp/net-${DEVICE}.conf
[ -z ${HOSTNAME} ] && HOSTNAME=${OLDHOSTNAME}
export HOSTNAME
# Check if we have a network device at all
if ! ls /sys/class/net/eth0 > /dev/null 2>&1 && \
! ls /sys/class/net/wlan0 > /dev/null 2>&1 && \
! ls /sys/class/net/ath0 > /dev/null 2>&1 && \
! ls /sys/class/net/ra0 > /dev/null 2>&1
then
panic "No supported network device found, maybe a
non-mainline driver is required."
fi
if [ "${NFSROOT}" = "auto" ]
then
NFSROOT=${ROOTSERVER}:${ROOTPATH}
fi
if ( [ -n "${FETCH}" ] || [ -n "${HTTPFS}" ] || [ -n
"${FTPFS}" ] ) && do_httpmount
then
rc=0
return ${rc}
fi
if [ "${NFSROOT#*:}" = "${NFSROOT}" ] && [ "$NETBOOT" !=
"cifs" ]
then
NFSROOT=${ROOTSERVER}:${NFSROOT}
fi
log_begin_msg "Trying netboot from ${NFSROOT}"
if [ "${NETBOOT}" != "nfs" ] && do_cifsmount
then
rc=0
elif do_nfsmount
then
NETBOOT="nfs"
export NETBOOT
rc=0
fi
log_end_msg
return ${rc}
}
for this to work you need at last another change to the "live" script.
Inside the function Arguments() at top of the "live" script you need to
add the BOOTIF parameter like this:
BOOTIF=*)
BOOTIF="${ARGUMENT#BOOTIF=}"
export BOOTIF
;;
After updateing the initramfs inside the chroot and copying after that
the initrd.img for the used install boot kernel into the tftpd root, my
problem was solved.
I know that this workaround doesn't look pretty and doesn't solve all
problems when you don't use the IPAPPEND parameter of pxelinux.
So, I'm going to do some more adjusting of the LTSP udhcpc script and
adjust it to all our needs.
udhcpc does not fail on vmwares with standard hardware and does not fail
on machines with be2net/be2iscsi nics. Tests with bnx2 and bnx2x nics
were also successful.
I'm thinking, that udhcpc is a better solution for dhcp requests inside
(live) initramfs and we should try to go that way.
I don't know what it needs to start the discussion with initramfs
upstream (or the linux plumbers) but we could also create a separate fai
initramfs overlay (just like the live one).
What do you think?
Regards,
\sh
--
Stephan '\sh' Adig
SysAdmin / Ubuntu Developer
xmpp: sh at sourcecode.de
More information about the linux-fai
mailing list