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