A boot server for network-booting old and unusual machines with many boot protocols, all set up from one configuration file.
gobootd is a Go reimplementation of bootd. It is not a drop-in replacement, but it follows the same idea: describe each client once (its addresses, the protocols it may use, and the files it may load), and let every protocol answer from that one description. Every request that is refused is logged with the reason.
Under development. The configuration and diagnostics tools work, and so do the first protocols. They are tested against recorded traffic and simulated clients, but not yet against real hardware.
| Protocol | Clients | Status |
|---|---|---|
| RARP | Sun, DEC, HP and others | works |
| RMP | HP 9000/300 and /400 | works |
| ND | Sun-2 | works (diskless SunOS 2.0) |
| TFTP | most | works (read only; blksize, tsize, timeout options) |
| bootparam | SunOS, NetBSD, Solaris | works (with a built-in portmapper) |
| NFSv2 | diskless root/swap | works (read only, MOUNT v1) |
| NIS | SunOS and other YP clients | works (read only, no map transfer) |
| RPL | IBM, 3Com DOS boot ROMs | planned |
| MOP | DEC VAX, Alpha | planned |
| BOOTP/DHCP | most | planned |
| SMB1 | DOS clients after RPL | planned |
| RBCS | 3Com 3Station | needs reverse engineering |
Packages are attached to each release, and available from:
- Debian and Ubuntu (amd64, arm64): the apt repository,
then
sudo apt install gobootd - macOS: the Homebrew tap,
brew install rpajarola/tap/gobootd, or the installer package from a release, which needs no Homebrew
packaging describes what is installed, how to set up the services, and how the packages are built.
Or build it with Go: go install github.com/rpajarola/gobootd/cmd/...@latest
(bootd and bootbridge need libpcap's headers).
The configuration is HCL. A short
example (see etc/bootd.hcl for more):
root = "/srv/netboot"
network "lab" {
address = "192.168.1.250/24" # bootd's own address on the network
udp = "127.0.0.1:4712" # frames to and from bootbridge, simh or QEMU
}
service "rarp" {}
service "tftp" {}
service "bootparam" {}
service "nfs" {}
host "kali" {
# mac and ip are optional: they are looked up in /etc/ethers and DNS.
services = ["rarp", "tftp", "bootparam", "nfs"]
file "files/sparc/solaris-2.5.1-sun4c" { name = "*" }
export "root" { path = "/export/hosts/kali/root" }
}bootd [run] [-c config] run the boot server
bootd check [-c config] [-q] validate the configuration and show what is served
bootd explain [-c config] <client> [service [file]]
show how a request from client would be handled
bootd check reports errors and warnings with file and line, then lists
every host with its addresses (and where they came from), services and
files. bootd explain follows one request through the same lookups the
protocols use, for example:
$ bootd explain kali tftp C0A80105.SUN4C
The default configuration file is /etc/bootd.hcl. Send SIGHUP to reload
hosts and files; service and log settings take effect on restart.
bootd works on Ethernet frames and runs its own IP stack (gVisor netstack) with its own MAC and IP address on each network. It answers ARP itself, and knows the MAC address of every configured host, so clients that do not answer ARP before their operating system runs can still be reached. It needs no privileges and does not touch the host's addresses or ports.
A network carries frames over UDP, one Ethernet frame per datagram with no header. This is the format of the HECnet bridge, which simh and QEMU speak:
-
Real machines:
bootbridgeconnects an interface to UDP. It is the only part that needs root (orCAP_NET_RAW, or access to/dev/bpf*):sudo bootbridge -i en0 -listen 127.0.0.1:4711 -peer 127.0.0.1:4712bootd then uses
udp = "127.0.0.1:4712"andpeers = ["127.0.0.1:4711"]. Several peers (bootd, simh, QEMU) can share one bridge. While it runs,bootbridgectl peersandbootbridgectl dumpshow who is attached and what passes through. -
simh:
attach xq udp:4713:127.0.0.1:4712 -
QEMU:
-netdev dgram,id=n0,local.type=inet,local.host=127.0.0.1,local.port=4713,remote.type=inet,remote.host=127.0.0.1,remote.port=4712
bootd learns peers that send to it, so several emulators can talk to one bootd without a bridge.
Instead of raw frames, a network can use static L2TPv3 (RFC 3931), which Linux and QEMU also speak. Its cookie keeps out anyone who does not know it, so bootd and bootbridge can run on different machines:
network "lab" {
address = "192.168.1.250/24"
udp = "0.0.0.0:1701"
peers = ["192.168.1.10:1701"] # sudo bootbridge -i en0 -l2tp 0.0.0.0:1701 -cookie 0x0123456789abcdef
l2tp { cookie = "0x0123456789abcdef" }
}bootd sends its fixed peers a keepalive every minute, so bootbridge needs
no -peer for it. See bootbridge for details.
A network can also capture directly with pcap = "en0", which needs
privileges. On Wi-Fi, where the access point drops frames from unknown MAC
addresses, add mac = "interface" to use the interface's own address (with
an IP address of bootd's own).
- RARP answers with bootd's address on the network.
- RMP: clients without a host entry are matched to a class by the machine
type their boot ROM sends (
match = { rmp_machtype = "HPS300" }). The boot ROM's file list shows the host's RMP files in configuration order. - ND: the Sun-2 PROM boots from public unit 0 (
ndp0) before it knows its IP address, so the host needs a MAC address. A disk withmode = "bootfile"works like NetBSD's ndbootd: the file inpathis the first-stage program (at most 15 blocks, e.g.bootyy), andboot2(e.g.netboot) starts at block 16. Disks inimagemode are matched by ND minor number inunit(64 + n forndp<n>). - bootparam tells a client its host name and where its exports are:
GETFILE rootanswers with the host'sexport "root", so bootparam and NFS always agree. Exports with aserverpoint to another NFS server. The portmapper is built in and answers broadcastCALLITrequests; unknown clients and unknown keys get no answer, so another server can answer.dumpgets an empty answer when not configured, as SunOS expects;gatewaygets the router and netmask, which NetBSD needs. - NFS serves the host's
exportblocks over NFSv2 and MOUNT v1 (UDP); a client may only mount its own exports. A root file system extracted without root privileges lacks device nodes and root-owned files: give the export aspec, an mtree(5) file such astar -cf root.mtree --format=mtree @root.tgzmakes, and bootd serves the owners, modes, devices and symlinks it lists.export_pathsets the path clients see; NetBSD keepsserver:pathin 90 bytes. Writes are not supported yet. - NIS serves one domain to hosts that may use
nis. The maps are made from files in/etcformat in themapsdirectory (passwd,group,hosts,ethers,netgroup,services,protocols,rpc,networks,netmasks,aliases,publickey,bootparams), keyed as SunOS's/var/yp/Makefilekeys them; a missing file means no map. The configured hosts are added tohosts.*andethers.*, and bootd's ownserver_namewith its address on each network. ypbind finds the server by broadcast;ypcatworks over TCP. bootparam announces the NIS domain to hosts that may usenis, so SunOS sets its domain name and starts ypbind. The files are read again on SIGHUP.netgroup.byuserandnetgroup.byhostare not made, and maps cannot be pushed to slaves. - TFTP identifies clients by IP address. A request for a path such as
/tftpboot/namealso matches a file namedname. Requests sent to a broadcast address are answered too, but refusals of broadcast requests are only logged, so another server can answer.
Options for each protocol are listed in etc/bootd.hcl.
The manual pages bootd(8), bootbridge(8),
bootbridgectl(1) and bootd.hcl(5)
describe every command, flag and setting (man ./man/bootd.8 reads one
from the source tree).
bootd and bootbridge need libpcap and cgo; bootbridgectl does not.
go build ./cmd/bootd ./cmd/bootbridge ./cmd/bootbridgectl
go test ./...
The tests need no privileges: protocols run on an in-memory segment, and an
end-to-end test boots a client through the daemon over UDP.
test/emulator boots emulated machines with their real
boot PROMs (not included) against bootd, e.g. a SPARCstation 5 in QEMU
that boots NetBSD diskless: RARP, TFTP, bootparam and NFS.
GPLv3, see LICENSE.