I just want to PXE-boot my PC with some image on a Debian-based server.

This should be the least-hassle method of booting a PC over the network from a Debian-based server using the Intel PXE interface.

I set this up to PXE-boot memtest86+, but anything that can be loaded via the PXELINUX loader (such as a Linux kernel) can be added to the menu and booted this way. Building the menu via PXELINUX avoids the byzantine and reportedly bug-ridden PXE menu facility.

Minumum Viable TFTP Server Configuration

Install and enable a TFTP server:

# apt-get install openbsd-inetd tftpd
# update-inetd --enable tftp

(or edit /etc/inetd.conf)

# killall -HUP inetd

Create the file hierarchy to boot via TFTP (menu config taken from here):

# mkdir -p /srv/tftpboot/pxelinux.cfg
# chmod a+rx /srv /srv/tftpboot /srv/tftpboot/pxelinux.cfg
# apt-get install syslinux-common
# ln -s /usr/lib/syslinux/pxeboot.0 /srv/tftpboot
# ln -s /usr/lib/syslinux/menu.c32 /srv/tftpboot
# cat > /srv/tftpboot/pxelinux.cfg/default
DEFAULT menu
PROMPT 0
MENU TITLE PXEBoot Server.  Use at own risk.
TIMEOUT 200
TOTALTIMEOUT 6000
ONTIMEOUT local
LABEL local
      MENU LABEL (local)
      MENU DEFAULT
      LOCALBOOT 0
MENU end
^D

Then verify:

# find /srv/tftpboot
/srv/tftpboot
/srv/tftpboot/pxelinux.0
/srv/tftpboot/menu.c32
/srv/tftpboot/pxelinux.cfg
/srv/tftpboot/pxelinux.cfg/default

Minimum Viable DHCP Boot Server Configuration

I’m using the ISC DHCP server version 3. The DHCP server should have at least this configuration in /etc/dhcp/dhcpd.conf for the subnet the booting PC is on, although you can safely adapt it to your needs.

Assumptions:

  • Client is Intel PXE ROM
  • Network is 10.0.2.0/24
  • DHCP server is 10.0.2.254
  • TFTP server is the same machine, 10.0.2.254
  • TFTP server is rooted at /srv/tftp
  • We are booting the PXELINUX kernel (pxelinux.0)
option space PXE;
option PXE.discovery-control code 6 = unsigned integer 8;
option PXE.boot-server code 8 = { unsigned integer 16,
                                  unsigned integer 8,
                                  ip-address };
option PXE.boot-menu code 9 = { unsigned integer 16,
                                unsigned integer 8,
                                text};
option PXE.menu-prompt code 10 = { unsigned integer 8, text };

shared-network "workstations" {
  subnet 10.0.2.0 netmask 255.255.255.0 {
  range 10.0.2.250 10.0.2.253;
  option subnet-mask 255.255.255.0;
  option broadcast-address 10.0.2.255;
  default-lease-time 1200;
  max-lease-time 7200;

  allow booting;

  # Order of parameter request list is important!
  # For PXE as early as 1.0, 60,43,67 are required; must be in that order; and must be first in the list.
  option dhcp-parameter-request-list 60,43,67;
  # A list that includes PXE as well as "normal" DHCP parameters such as DNS servers, gateway, etc:
  #option dhcp-parameter-request-list 60,43,67,1,3,6,12,15,17,26,28,40,119;

  # Configure the PXE DHCP options now:
  # Option 60
  option vendor-class-identifier "PXEClient";
  # Option 43
  vendor-option-space PXE;
  option PXE.discovery-control 15;  # Disable menus and server discovery; use provided file only.
  option PXE.boot-server 15 1 10.0.2.254;
  # Option 67
  option bootfile-name "/srv/tftp/pxelinux.0";

  # Other netboot-related parameters that can be played with if necessary:
  # Option 66 - unused by PXE
  #option tftp-server-name "10.0.2.254";

  # BOOTP parameters - unused by PXE
  #allow bootp;
  #server-name "10.0.2.254";
  #next-server 10.0.2.254;
  #filename "/srv/tftp/pxelinux.0";
}

After adding/editing the dhcpd.conf, restart the DHCP server:
# service isc-dhcp-server restart

You should now be able to boot into the PXELINUX menu. From here, you can add menu options such as the aforementioned memtest86 (note: PXE cannot boot a gzip file).

Troubleshooting

Dang, it didn’t work! Well, aside from googling the various opaque PXE error messages, there are a few things you can try:

Check logs

/var/log/dhcpd.log and /var/log/syslog may have useful information from the DHCP server and TFTP server (respectively).

dhcpdump

dhcpdump will parse DHCP/BOOTP messages into human-readable format.

strace

Try to strace -f the inetd process as it launches the TFTP server to see what’s going on.

tcpdump

tcpdump -A -n -i <interface> udp
This will give you ASCII dumps of all UDP packets, which will be helpful if the client is doing strange things. For example, attempting to contact a non-existent “proxyDHCP” server on port 4011, or making an unexpected file request. Example:

08:22:01.271402 IP 10.0.2.252.2070 > 10.0.2.254.69:  42 RRQ "/srv/tftp/pxelinux.0^A^DM-^?M-^?M-^?" octet tsize 0

…uh, what?

PXE ROM bugs

The Intel Pro/100 NIC I was using shows the following banner at boot:

Intel UNDI, PXE-1.0 (build Dev)
Copyright (C) 1997,1998 Intel Corporation

PXE 1.0 is a very old version, and has at least one bug: in this setup at least, it requests the boot filename the DHCP server provided it, but with trailing garbage. Of course, a boot file named with trailing garbage is never found on the TFTP server, and so the boot fails.

How to fix it? I wrote a tiny C program to create a symbolic link from the real boot filename to the garbled request filename, which I determined with precision using strace on the TFTP server.

#include <stdio.h>

int main(void) {
  return symlink("pxelinux.0", "pxelinux.0\001\004\377\377\377");
}

A general solution appears elusive.

Leave a Reply