Netboot Shenanigans: Mac network boot using ISC-DHCP as a BSDP server

This article by Pepijn Oomen describes how to get a netboot image to show up in the ⌥‑boot menu of the MacMini3,1, using nothing but the standard ISC‑DHCP server.

Why would you do this? Well, the ISC‑DHCP server is the standard DHCP server in many Linux distributions. :)

This method worked great until I updated the firmware on my iMac12,1. I could still boot the image, but it would not appear in the boot menu.

However, I was able to get it working by modifying the BSDP packet.

Pepijn’s configuration has the following characteristics:

  • When the Mac requests the image list, the DHCP server returns the list of images available on the server. In Pepijn’s case, one image is listed, with id “1” and name “netboot”:

option vendor-encapsulated-options
      01:01:01:               # bsdp_msgtype_list
      09:0c:81:00:00:01:07:6e:65:74:62:6f:6f:74; # netboot
  • Otherwise, it returns the select boot image (index 1):

option vendor-encapsulated-options
      01:01:02:               # bsdp_msgtype_select
      08:04:81:00:00:01;      # bsdptag_selected_boot_image

This worked fine with the new firmware when the Mac was booted holding ‘N’, but to get it to appear in the ⌥‑boot menu, I had to make the following modifications:

  • I modified the bsdp_msgtype_list so the id and name matched my image, and I added the server priority (BSDP option 4) and default image (BSDP option 7):

# bsdp image list message:
# one image, plus one default image (both are the same)
option vendor-encapsulated-options 
    01:01:01:                                 # bsdp_msgtype_list
    04:02:                                    # bsdp option code 4 (length 02) server priority
        80:00:                                #     Priority (32768)
    07:04:                                    # bsdp option code 7 (length 04) default image id
        81:00:00:89:                          #     Image ID (137)
    09:0e:                                    # bsdp option code 9 (length 0e) image list
        81:00:00:89:                          #     Image ID (137)
            09:54:68:65:2d:49:6d:61:67:65;    #         Name (length 09) 'The-Image'
  • I added the machine name to the bsdp_msgtype_select reply (and modified the index to match the change above).

# details about the selected image
# 
option vendor-encapsulated-options
    01:01:02:                          # bsdp_msgtype_select 
        08:04:                             # bsdptag_selected_boot_image
            81:00:00:89:                   #     Image ID (137)
        82:09:                             # Machine Name (length 09)
            54:68:65:2d:49:6d:61:67:65;    #     'The-Image'

… voilà! The image appears in the boot menu.

The following pages were extremely helpful when figuring out how to modify the bsdp packets:

Finally, I have included the entire dhcpd.conf below. It also includes a (commented) message list which contains multiple images. The two images appear separately in the boot menu, but I can’t figure out an easy way to differentiate between them during bsdp_msgtype_select.

#
# Apple BSDP server. Does NOT give out IP addresses
#

ddns-update-style none;
ddns-updates off;
ignore client-updates;
allow booting;
authoritative;
boot-unknown-clients on;
ping-check off;
one-lease-per-client on;
default-lease-time 7200;
max-lease-time 7200;
allow-unknown-clients;

subnet 0.0.0.0 netmask 0.0.0.0 { 
    pool {
        # DON'T supply an IP address!
        range 0.0.0.0 0.0.0.0;
        allow members of "netboot";
    }
}

class "netboot" {

    match if substring (option vendor-class-identifier, 0, 9) = "AAPLBSDPC";
    option dhcp-parameter-request-list 1,3,17,43,60;

    if (option dhcp-message-type = 1) {
        option vendor-class-identifier "AAPLBSDPC";
        option vendor-encapsulated-options
            08:04:81:00:00:89;    # bsdp option 8 (length 04) -- selected image id;
    } elsif (option dhcp-message-type = 8) {
        option vendor-class-identifier "AAPLBSDPC";
        if (substring(option vendor-encapsulated-options, 0, 3) = 01:01:01) {
            log(debug, "bsdp_msgtype_list");

            # bsdp image list message:
            # one image, plus one default image (both are the same)
            option vendor-encapsulated-options 
                01:01:01:                              # bsdp_msgtype_list
                04:02:                                 # bsdp option code 4 (length 02) server priority
                    80:00:                             #  Priority (32768)
                07:04:                                 # bsdp option code 7 (length 04) default image id
                    81:00:00:89:                       #  Image ID (137)
                09:0e:                                 # bsdp option code 9 (length 0e) image list
                    81:00:00:89:                       #  Image ID (137)
                        09:54:68:65:2d:49:6d:61:67:65; #   Name (length 09) 'The-Image'

            # bsdp image list message:
            # TWO images, plus one default image (both are the same)
            #option vendor-encapsulated-options 
            #    01:01:01:        # bsdp_msgtype_list
            #    04:02:           # bsdp option code 4 (length 02) -- server priority
            #        80:00:       #  Priority (32768)
            #    07:04:           # bsdp option code 7 (length 04) -- default image id
            #        81:00:00:89: #  Image ID (137)
            #    09:1b:           # bsdp option code 9 (length 1b) -- image list
            #        81:00:00:89: #  Image ID (137)
            #            09:54:68:65:2d:49:6d:61:67:65: # Name (length 09) 'The-Image'
            #        81:00:04:31: #  Image ID (1073)
            #            08:44:53:52:2d:31:30:37:33;    # Name (length 08) 'DSR-1073'
            #
            # option-boot lists both images, but the "root-path"
            # below is hardcoded! So it doesn't actually support
            # both images -- it'll always boot the first image. :(

        } else {
            log(debug, "bspd_msgtype_select");

            # details about the selected image
            # 
            option vendor-encapsulated-options
                01:01:02:                       # bsdp_msgtype_select 
                08:04:                          # bsdptag_selected_boot_image
                    81:00:00:89:                #  Image ID (137)
                82:09:                          # Machine Name (length 09)
                    54:68:65:2d:49:6d:61:67:65; #  'The-Image'

            if (substring(option vendor-class-identifier, 10, 4) = "i386") {
                filename "/osx/i386/booter";
                next-server 10.25.64.32;
                option root-path = "http://10.25.64.32/build.sparseimage";
            } elsif (substring(option vendor-class-identifier, 10, 3) = "ppc") {
                filename "nil";
            }
        }
    }
}

Update 06 Feb 2013: Brandon’s Tinkerings has a solution for handling multiple images correctly. Awesome! :)

Update 21 Feb 2013: The server priority is in fact BSDP option 4, not option 2.

Update 09 April 2014: Brandon’s Tinkerings appears to be offline. :( Mirror here.

Update: Brandon’s Tinkerings is back up again! :)