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