Dnsmasq - PXE boot using Netboot.xyz

I'm trying to set configure dnsmasq to point dhcp clients to a PXE server running on another machine. Specifically, the PXE server is running Netboot.xyz as a container within docker. The intention is to have the ability to load different bootloaders based on the architecture used by the client.

Example:
legacy 32bit bios = load netboot.xyz.kpxe
UEFI 32/64bit = load netboot.xyz.efi

I found [THIS] regarding Architecture detection within PXE, which explains what integers specify what architecture

the creator of this container has some documentation [HERE] on how to set this up with OpenWRT but their uci commands do not produce the intended results.

uci set dhcp.@dnsmasq[0].dhcp_match=set:bios,60,PXEClient:Arch:00000
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:bios,netboot.xyz.kpxe,,YOURSERVERIP
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi32,60,PXEClient:Arch:00002
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi32,netboot.xyz.efi,,YOURSERVERIP
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi32-1,60,PXEClient:Arch:00006
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi32-1,netboot.xyz.efi,,YOURSERVERIP
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi64,60,PXEClient:Arch:00007
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi64,netboot.xyz.efi,,YOURSERVERIP
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi64-1,60,PXEClient:Arch:00008
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi64-1,netboot.xyz.efi,,YOURSERVERIP
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi64-2,60,PXEClient:Arch:00009
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi64-2,netboot.xyz.efi,,YOURSERVERIP
uci commit
/etc/init.d/dnsmasq restart

It looks like the each pair of commands simply overwrites the previous dhcp_match= and dhcp_boot= settings rather than add to the list. so I just edited /etc/config/dhcp and added the lines manually. (would this be the proper time to use "uci add_list ... "?)

When I attempt to boot a client machine (Virtualbox PXE Client) it just sits at the screen where it's contacting the dhcp server and does nothing.

I'm not sure what other information i should provide. Let me know.

1 Like

It doesn't look right. I would increase the @dnsmasq[x] by 1 for each case.

Not sure if you found the answer but here is what you need.

tee -a /etc/dnsmasq.conf << EOF
dhcp-match=set:ipxeclient,60,IPXEClient*
dhcp-match=set:bios,60,PXEClient:Arch:00000
dhcp-boot=tag:bios,netboot.xyz.kpxe,,192.168.8.1
dhcp-match=set:efi32,60,PXEClient:Arch:00002
dhcp-boot=tag:efi32,netboot.xyz.efi,,192.168.8.1
dhcp-match=set:efi32-1,60,PXEClient:Arch:00006
dhcp-boot=tag:efi32-1,netboot.xyz.efi,,192.168.8.1
dhcp-match=set:efi64,60,PXEClient:Arch:00007
dhcp-boot=tag:efi64,netboot.xyz.efi,,192.168.8.1
dhcp-match=set:efi64-1,60,PXEClient:Arch:00008
dhcp-boot=tag:efi64-1,netboot.xyz.efi,,192.168.8.1
dhcp-match=set:efi64-2,60,PXEClient:Arch:00009
dhcp-boot=tag:efi64-2,netboot.xyz.efi,,192.168.8.1

EOF
uci set dhcp.@dnsmasq[0].dhcp_match=set:bios,60,PXEClient:Arch:00000
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:bios,ipxe/netboot.xyz.kpxe,,192.168.8.1
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi32,60,PXEClient:Arch:00002
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi32,ipxe/netboot.xyz.efi,,192.168.8.1
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi32-1,60,PXEClient:Arch:00006
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi32-1,ipxe/netboot.xyz.efi,,192.168.8.1
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi64,60,PXEClient:Arch:00007
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi64,ipxe/netboot.xyz.efi,,192.168.8.1
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi64-1,60,PXEClient:Arch:00008
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi64-1,ipxe/netboot.xyz.efi,,192.168.8.1
uci set dhcp.@dnsmasq[0].dhcp_match=set:efi64-2,60,PXEClient:Arch:00009
uci set dhcp.@dnsmasq[0].dhcp_boot=tag:efi64-2,ipxe/netboot.xyz.efi,,192.168.8.1
uci commit
/etc/init.d/dnsmasq restart

Change the IP to what your server is.

1 Like

I was also having the same issue described by OP when setting multiple dhcp-match / dhcp-boot options via uci commands, so I decided to take full control and edit /etc/dnsmasq.conf directly (see https://openwrt.org/docs/guide-user/base-system/dhcp.dnsmasq#etcdnsmasqconf)

I ended up adding this uncomplicated block in there:

###################################
# TFTP Server custom configutation
####################################
enable-tftp
#tftp-root=/root/tftp

# Set tags based on client arch
dhcp-match=set:bios,option:client-arch,0
#dhcp-match=set:efi-i32,option:client-arch,2
#dhcp-match=set:efi-i32,option:client-arch,6
#dhcp-match=set:efi-i64,option:client-arch,7
#dhcp-match=set:efi-i64,option:client-arch,8
#dhcp-match=set:efi-i64,option:client-arch,9
#dhcp-match=set:efi-a32,option:client-arch,10
#dhcp-match=set:efi-a64,option:client-arch,11

# Use kpxe file for legacy bios clients
dhcp-boot=tag:bios,http://boot.netboot.xyz/ipxe/netboot.xyz.kpxe

# Use efi file for anything else (ie: efi clients).
# Notice the negated 'bios' tag
dhcp-boot=tag:!bios,http://boot.netboot.xyz/ipxe/netboot.xyz.efi

That block allows legacy bios clients to boot from http://boot.netboot.xyz/ipxe/netboot.xyz.kpxe and any other non legacy bios clients -UEFI clients basically- to boot from http://boot.netboot.xyz/ipxe/netboot.xyz.efi.

Since the boot files are pulled from the netboot.xyz project via http, I didn't even need to set the tftp-root setting in my dnsmasq.conf.

You can definitely have more fine grained control on which dhcp-boot files get assigned to which client arch tags. In my example above I'm only using the bios tag and its negated form !bios to discriminate, but you could uncomment the other dhcp-match entries and add new dhcp-boot lines to suit your needs.

Please note that if you configure TFTP settings via /etc/dnsmasq.conf then make sure you disable the TFTP settings in LUCI (or its underlying config file /etc/config/dhcp) so there's no conflict between them.

Finally to apply the changes, run:

/etc/init.d/dnsmasq restart

3 Likes

This threw me off at first, but I figured that the second part is not necessary. Editing /etc/dnsmasq.conf is sufficient. Thanks.

I found this thread while struggling with my own netboot.xyz setup, and I'd like to leave a few tips for those who come after me. Most of the guides online are incomplete in one way or the other, especially when applied to OpenWrt.

  1. In /etc/config/dockerd, the last line is not present by default, but it's crucial. Without it, all traffic forwarding from/to the container will be completely blocked.
config firewall 'firewall'
        option device 'docker0'
        list blocked_interfaces 'wan'
        option extra_iptables_args '--match conntrack ! --ctstate RELATED,ESTABLISHED'
  1. When using docker compose to create the container from the sample .yaml file, it gets joined to a new network, which has no forwarding to WAN, i.e., no way of downloading assets. The .yaml file must have network mode set to bridge to join the preexisting docker bridge network instead.
services:
  netbootxyz:
    image: ghcr.io/netbootxyz/netbootxyz
    container_name: netbootxyz
    environment:
#      - MENU_VERSION=2.0.84 # optional
      - NGINX_PORT=80 # optional
      - WEB_APP_PORT=3000 # optional
#    volumes:
#      - ./config:/config # optional
#      - ./assets:/assets # optional
    ports:
      - 192.168.1.1:3000:3000  # optional, destination should match ${WEB_APP_PORT} variable above.
      - 192.168.1.1:69:69/udp
      - 192.168.1.1:8080:80  # optional, destination should match ${NGINX_PORT} variable above.
    network_mode: bridge
    restart: unless-stopped

Then allow forwarding from the docker zone to WAN in the firewall.
3. dnsmasq requires a completely different way of adding boot entries and tags via UCI than shown above. This script must be used instead:

#!/bin/sh
uci -q batch << EOI
add dhcp boot
set dhcp.@boot[-1].filename="netboot.xyz.kpxe"
set dhcp.@boot[-1].serveraddress="192.168.1.1"
set dhcp.@boot[-1].networkid="bios"
set dhcp.@boot[-1].servername="netbootxyz"
add dhcp boot
set dhcp.@boot[-1].filename="netboot.xyz.efi"
set dhcp.@boot[-1].serveraddress="192.168.1.1"
set dhcp.@boot[-1].networkid="efi32"
set dhcp.@boot[-1].servername="netbootxyz"
add dhcp boot
set dhcp.@boot[-1].filename="netboot.xyz.efi"
set dhcp.@boot[-1].serveraddress="192.168.1.1"
set dhcp.@boot[-1].networkid="efi32-1"
set dhcp.@boot[-1].servername="netbootxyz"
add dhcp boot
set dhcp.@boot[-1].filename="netboot.xyz.efi"
set dhcp.@boot[-1].serveraddress="192.168.1.1"
set dhcp.@boot[-1].networkid="efi64"
set dhcp.@boot[-1].servername="netbootxyz"
add dhcp boot
set dhcp.@boot[-1].filename="netboot.xyz.efi"
set dhcp.@boot[-1].serveraddress="192.168.1.1"
set dhcp.@boot[-1].networkid="efi64-1"
set dhcp.@boot[-1].servername="netbootxyz"
add dhcp boot
set dhcp.@boot[-1].filename="netboot.xyz.efi"
set dhcp.@boot[-1].serveraddress="192.168.1.1"
set dhcp.@boot[-1].networkid="efi64-2"
set dhcp.@boot[-1].servername="netbootxyz"
add dhcp boot
set dhcp.@boot[-1].filename="netboot.xyz-arm64.efi"
set dhcp.@boot[-1].serveraddress="192.168.1.1"
set dhcp.@boot[-1].networkid="efi64-3"
set dhcp.@boot[-1].servername="netbootxyz"
add dhcp vendorclass
set dhcp.@vendorclass[-1].networkid="bios"
set dhcp.@vendorclass[-1].vendorclass="PXEClient:Arch:00000"
add dhcp vendorclass
set dhcp.@vendorclass[-1].networkid="efi32"
set dhcp.@vendorclass[-1].vendorclass="PXEClient:Arch:00002"
add dhcp vendorclass
set dhcp.@vendorclass[-1].networkid="efi32-1"
set dhcp.@vendorclass[-1].vendorclass="PXEClient:Arch:00006"
add dhcp vendorclass
set dhcp.@vendorclass[-1].networkid="efi64"
set dhcp.@vendorclass[-1].vendorclass="PXEClient:Arch:00007"
add dhcp vendorclass
set dhcp.@vendorclass[-1].networkid="efi64-1"
set dhcp.@vendorclass[-1].vendorclass="PXEClient:Arch:00008"
add dhcp vendorclass
set dhcp.@vendorclass[-1].networkid="efi64-2"
set dhcp.@vendorclass[-1].vendorclass="PXEClient:Arch:00009"
add dhcp vendorclass
set dhcp.@vendorclass[-1].networkid="efi64-3"
set dhcp.@vendorclass[-1].vendorclass="PXEClient:Arch:0000B"
commit dhcp
EOI

I tried to cover all vendorclass options, but only tested 64-bit UEFI, which works as intended.
4. I'm pretty sure you need iptables-nft installed, as docker has no direct support for nftables.
5. If changed, network-related settings from /etc/config/dockerd will not reflect in iptables even after rebooting dockerd. Only after a system reboot, but there could be a better way.

I hope this is helpful. And, of course, if someone more familiar with docker reads this, let me know if I'm still missing something.

2 Likes