Chainloading (patched) iPXE firmware with iPXE

      No Comments on Chainloading (patched) iPXE firmware with iPXE

These days, quite a few devices already come with built-in iPXE support: Some hypervisors (qemu, Proxmox, VMWare, VirtulBox, etc.) support iPXE right out of the gate but there’s also an increasing number of network equipment vendors that sell network interface cards with bundled iPXE support.

But what if the bundled iPXE firmware is out of date and/or lacking a relevant feature? Recompiling iPXE is not particularly difficult but changing the iPXE rom file on a hypervisor might be. Or it might be inadvisable due to a support contract. And flashing new rom files onto network interface cards is usually cumbersome and very time consuming.

So how about using iPXE chainloading to load a patched or otherwise customised iPXE rom?

Chainloading iPXE via ISC DHCP

iPXE chaingloading is quite a common technique to dynamically load an iPXE rom onto a device that otherwise would not support iPXE. It’s usually used to load iPXE from a PXE environment. To break the resulting infinite loop the DHCP server needs to be configured to hand out an iPXE rom to PXE clients but a “real” boot configuration to iPXE clients:

next-server your-tftp-server;

if exists user-class and option user-class = "iPXE" {
    filename "http://your-http-server/ipxe/boot.ipxe";
} elsif option arch = 00:07 {
    # EFI x86-64 (Intel x86 64-bit EFI mode)
    # - most commonly used on newer hardware
    filename "ipxe-x86_64.efi";
} elsif option arch = 00:06 {
    # EFI IA32 (Intel x86 32-bit EFI mode)
    # - almost never seen in the wild
    filename "ipxe-i386.efi";
}  else {
    # Intel x86PC (Intel x86 32-bit legacy BIOS mode)
    # - technically option arch = 00:00, but we use it as fallback
    filename "undionly.kpxe";
}

Assuming we’ve successfully recompiled our iPXE roms with the desired patches or enhancements we can expand on this to hand out a newer iPXE rom even to iPXE client. Making use use of some iPXE specific dhcp options:

next-server your-tftp-server;

# http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.txt
option arch code 93 = unsigned integer 16;

option space ipxe;
option ipxe.version code 235 = string;

if exists ipxe.version and binary-to-ascii(10, 8, ".", option ipxe.version) = "1.21.1" {
    filename "http://your-http-server/ipxe/boot.ipxe";
} elsif option arch = 00:07 {
    # EFI x86-64 (Intel x86 64-bit EFI mode)
    # - most commonly used on newer hardware
    filename "ipxe-x86_64-fixed.efi";
} elsif option arch = 00:06 {
    # EFI IA32 (Intel x86 32-bit EFI mode)
    # - almost never seen in the wild
    filename "ipxe-i386-fixed.efi";
}  else {
    # Intel x86PC (Intel x86 32-bit legacy BIOS mode)
    # - technically option arch = 00:00, but we use it as fallback
    filename "undionly-fixed.kpxe";
}

Documentation on ISC DHCP conditional evaluation can be found here: https://linux.die.net/man/5/dhcpd-eval

To bump the version number when compiling a new rom:

$ git clone https://github.com/ipxe/ipxe
$ cd ipxe/src
$ make VERSION_PATCH=2

On could, of course, also bump VERSION_MAJOR or VERSION_MINOR or use EXTRAVERSION


iPXE chainloading an updated version via dhcp

Chainloading iPXE via iPXE script

Sometimes modifying the dhcp server configuration might not be possible or practical in a real world application, e.g. because you don’t have control over the dhcp server or because it’s a Microsoft Windows DHCP server. But we can use iPXE to determine its current version and then potentially handing out an updated iPXE rom.

Let’s assume the dhcp server is handing out http://your-http-server/ipxe/boot.ipxe through the filename option and that particular file reads:

#!ipxe

echo Booting Linux...
kernel your-kernel-image.img
initrd your-initramfs.img

boot

We can modify this to include an iPXE version check:

#!ipxe

chain --autofree version_check.ipxe ||

echo Booting Linux...
kernel your-kernel-image.img
initrd your-initramfs.img

boot

The version check might look like something similar to this (source):

#!ipxe

# Figure out if client is 64-bit capable
cpuid --ext 29 && set arch x64 || set arch x86
cpuid --ext 29 && set archl amd64 || set archl i386

set latest_version 1.21.1+ (g6ba67)
echo ${cls}
iseq ${version} ${latest_version} && exit ||
echo
echo Updated version of iPXE is available:
echo
echo Running version.....${version}
echo Updated version.....${latest_version}
echo
echo Attempting to chain to latest version...

iseq ${platform} efi && goto is_efi || goto not_efi

:is_efi
iseq ${archl} amd64 && chain --autofree ${boot-url}ipxe-x86_64-fixed.efi || chain --autofree ${boot-url}ipxe-i386-fixed.efi

:not_efi
chain --autofree ${boot-url}undionly-fixed.kpxe ||

Of course, one needs to ensure that the server at boot-url hands out the fixed versions of the iPXE roms.


iPXE chainloading an updated version via iPXE script

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.