Many of us have experienced the pain that is a Raspberry Pi with a corrupted SD card. I suspect the erase-on-write nature of flash memory is responsible for much of the problem. Regardless of the cause, one solution is to use PXE booting with the Raspberry Pi 3. That’s a fancy way to say we’ll be booting the Raspberry Pi over the network, instead of from an SD card.
What does this have to do with Hacking My House? As I discussed last time, I’m using Raspberry Pi as Infrastructure by building them into the walls of every room in my house. You don’t want to drag out a ladder and screwdriver to swap out a misbehaving SD card, so booting over the network is a really good solution. I know I promised we’d discuss cabling and cameras. Think of this as a parenthetical article — we’ll talk about Ethernet and ZoneMinder next time.
So let’s dive in and see what the Preboot Execution Environment (PXE) is all about and how to use PXE with Raspberry Pi.
Getting Familiar with Raspberry Pi’s PXE Boot Feature
New Raspberry Pis ship with PXE boot enabled, allowing the Pi to load its file system from a server on the same network. My experience is that the Pi 3 model B+ boots more reliably than the older version. Either way, it’s a good idea to begin by running rpi-update. Additionally, you may need to manually enable PXE by booting once off an SD card with an option added to config.txt. The official write-up is a great guide, and one of the resources I used when I stumbled through the PXE process for the first time. However, I will be doing things just a bit differently.
Take a moment to consider your network layout. Will your Raspberry Pi connect to your existing network, or to a new, dedicated network? To use your primary network, your router must support custom DHCP options. An OpenWrt router or similar device has this capability, but a stock D-Link or Linksys probably does not.
You will need a server on your home network to provide the file systems for the PXE Pis. There are multiple approaches, and I’ll be using CentOS 7. The hardware for this could be an old desktop, a virtual machine, or even another Raspberry Pi. We’ll assume a server with two interfaces — one connected to your primary network, and the second, with a static IP of 192.168.2.1, dedicated to the Raspberry Pi network. (If you plan to use a single network, skip the dnsmasq steps below, and use your server’s existing IP address instead of 192.168.2.1.)
Housekeeping and Preparation
The code block below takes care of the initial dependencies and firewall settings, and enables the needed services. On the server, we need three primary services: Dnsmasq for DHCP, nfs-utils for NFS, and tftp-server for TFTP. Xinetd is a helper service for tftp-server, and we need unzip and wget for downloading and extracting the Raspbian disk image.
Install your favorite text editor (I’m using nano), and the policycoreutils package in order to work with SELinux. Then, allow the services through the firewall and set them to run automatically on boot. Lastly in this block of instructions, we’re setting SELinux to a more relaxed stance concerning the TFTP service.
sudo yum install -y dnsmasq tftp-server nfs-utils xinetd unzip wget nano policycoreutils-python sudo firewall-cmd --permanent --add-service nfs3 sudo firewall-cmd --permanent --add-service mountd sudo firewall-cmd --permanent --add-service rpc-bind sudo firewall-cmd --permanent --add-service tftp sudo firewall-cmd --permanent --add-service dhcp sudo systemctl enable xinetd sudo systemctl enable dnsmasq sudo systemctl enable rpcbind sudo systemctl enable nfs-server sudo setsebool -P tftp_home_dir 1
Set up the directory structure and a pair of temporary mount points, and then download and extract the latest Raspbian image.
sudo mkdir /tftpboot sudo mkdir -p /nfs/raspi1 mkdir ~/bootmnt mkdir ~/rootmnt wget http://director.downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip unzip 2018-06-27-raspbian-stretch-lite.zip
Booting with PXE starts with DHCP option 66 (not order 66, that’s something different), which points to the TFTP server. We’ll configure dnsmasq to add that option, as well as the listen interface and the IP address range. Change these values as is appropriate in your case, and add to /etc/dnsmasq.conf.
interface=ens9 dhcp-range=192.168.2.50,192.168.2.150,12h dhcp-option=66,192.168.2.1
The configuration for NFS is simple. We specify what directory we want exported by editing /etc/exports.
Xinetd stands for “eXtended InterNET Daemon.” This one service controls several other services, including TFTP. We’ll configure xinetd’s config file at /etc/xinetd.d/tftp looking for the lines “server_args” and “disable”. We’re adding the two “-v” flags to increase the verbosity, as we will need to see what files the Raspberry Pi is looking for as it boots.
server_args = -v -v -s /tftpboot disable = no
Building the Pi’s Boot Image
Kpartx is an invaluable tool to keep in your Linux-fu toolbox. We’ll use it to mount the partitions contained in this disk image, and then copy the file system to the new PXE root. The Pi looks first for bootcode.bin, so we also copy that file into place.
sudo kpartx -a -v 2018-06-27-raspbian-stretch-lite.img sudo mount /dev/mapper/loop0p2 ~/rootmnt/ sudo mount /dev/mapper/loop0p1 ~/bootmnt/ sudo cp -a ~/rootmnt/* /nfs/raspi1 sudo cp -a ~/bootmnt/* /nfs/raspi1/boot/ sudo cp -a /nfs/raspi1/boot/bootcode.bin /tftpboot/
We can also customize the filesystem for the Pi. First, to enable ssh access.
sudo touch /nfs/raspi1/boot/ssh
Next, we’ll modify the kernel boot line, at /nfs/raspi1/boot/cmdline.txt. Here we’re informing the kernel that it shouldn’t look for filesystem on a local disk, but to mount an NFS share as its root.
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=192.168.2.1:/nfs/raspi1,udp,v3 rw ip=dhcp rootwait elevator=deadline rootfstype=nfs
And finally, we remove a couple lines from the Pi’s fstab, so it doesn’t try to mount filesystems from the non-existent SD card. Edit /nfs/raspi1/etc/fstab and remove the last two lines, that mount
Network Sleuthing and Bind Mounts
At this point, we’re ready to see if our hard work has paid off. Reboot the server so the pending changes are applied. (Yes, it’s fairly easy to apply them by hand, without the reboot, but this step also tests that everything comes back up correctly on power loss.) Once it’s back, watch the system log while powering the Pi connected to the new network. (Al Williams just ran an article on the versatility of the tail command that’s worth a look.)
$ sudo tail -f /var/log/messages Sep 24 11:05:15 PXE xinetd: START: tftp pid=4824 from=192.168.2.103 Sep 24 11:05:15 PXE in.tftpd: RRQ from 192.168.2.103 filename bootcode.bin Sep 24 11:05:15 PXE in.tftpd: Client 192.168.2.103 finished bootcode.bin Sep 24 11:05:15 PXE in.tftpd: RRQ from 192.168.2.103 filename bootsig.bin Sep 24 11:05:15 PXE in.tftpd: Client 192.168.2.103 File not found bootsig.bin Sep 24 11:05:15 PXE in.tftpd: sending NAK (1, File not found) to 192.168.2.103 Sep 24 11:05:15 PXE in.tftpd: RRQ from 192.168.2.103 filename 57b3548e/start.elf
The date will differ, as will the “PXE”– the hostname of this server. You’re looking for the RRQ from an IP address. If that shows up, then everything is going great. If not, time for some troubleshooting.
You may notice that your Pi is not actually booting. That’s because we’re not quite done. Look back at the TFTP log lines, because we need the folder name your Pi is looking for, which happens to be its serial number. Create that folder inside the tftpboot folder. Replace 57b354e with the folder name from your log.
sudo mkdir /tftpboot/57b3548e
We need to talk about bind mounts, and why we’re not just copying files into this folder. Raspbian has a built-in procedure to update the kernel, through apt-get. PXE transfers the kernel stored in the TFTP folder, while the rest of the system is mounted as an NFS share. This arrangement prevents the Pi from updating its own kernel. There are a couple ways to fix this, but today we’re using the bind mount. Think of it as a hard link for directories. We’ll add a line to the server’s /etc/fstab, once again using the Pi’s serial number as the folder name.
/nfs/raspi1/boot /tftpboot/57b3548e none defaults,bind 0 0
And then use the mount command to make the bind mount live.
sudo mount /tftpboot/57b3548e
Now, power cycle the Pi, and it should come to life!
The Power of PXE and Pi
The number one problem with Raspberry Pi systems is SD card problems. Booting over the network will get around this weakness (assuming your network and your server are both stable systems). For my purposes this is a perfect choice, since I plan to use a Raspberry Pi in a three-gang electrical box, connected via Ethernet and using Power Over Ethernet. That said, any application where you will have an Ethernet connection, and need your Raspberry Pi to be reliable over the long term, is a good candidate for PXE.
Where do we go from here? Next time we’ll set up Zoneminder, and finally put these Pis to use. Let us know what else you want to see, or what we missed this time. Until then, happy hacking!