top of page
  • Linkedin

Orchestration and Services

Public·1 member

Configuring a DHCP server

Configuring a DHCP server


In the previous article the remote root filesystem was mounted by the kernel witch itself was loaded by U-boot over HTTP. Both the kernel and root filesystem are served from the development host.


However, there is still a big issue to solve: this setup works only on my home network which has the following netid: 172.22.22.0/24. Many places use the 192.168.1.0/24 netid. In the current setup, the Pi obtains an IP address from the DHCP server running on my home router. This means that uboot.scr will have to be modified when my location changes and I have to remove the SD-Card, mount it, modify and re-generate the Uboot script, unmount - not practical at all.


There are several solutions to this problem, the simplest one is to use a second dedicated network adapter and connect the Pi to it. Configure systemd-networkd to assign a static IP address to the network adapter. And in U-boot script configure the network card to use a static IP address, this way the server IP address in boot.scr can remain hardcoded. Because the netid will always be the same.


A more advanced solution is to create a separate network, with a DHCP server listening ONLY on the dedicated adapter. Going forward we might need to have access to the internet on the Pi and in most scenarios, it is also generally a good idea to use DHCP to avoid manually setting static IP addresses on network cards - So I will go with this option.


The downside of both approaches is that not everyone has access to a spare USB network adapter at all times.


The dedicated network adapter is required for this use case as one doesn't always control of the network configuration. There is good news, support for mDNS is currently being added to U-Boot. So there might be a way to avoid using a dedicated network adapter in the future.


https://github.com/u-boot/u-boot/tree/master/lib/lwip/lwip/src/apps/mdns


Configuring the dedicated network adapter


Ubuntu uses the systemd-networkd service to configure the network cards, my dedicated network has the following MAC address: 48:65:ee:1d:a7:51


It has been assigned the following name: enx4865ee1da751 - so let's configure it differently 😀


The network adapters are configured with files present in /etc/systemd/network


Let's create a .link file for the dedicated adapter


sudo vim /etc/systemd/network/15-rpi-lan.link

With the following content

[Match]
MACAddress=48:65:ee:1d:a7:51

[Link]
Name=rpilan0
MACAddress=00:11:22:33:44:55

This assigns the name rpilan0 to the network adapter and sets a custom MAC address. The setting of the custom MAC address is not mandatory, but it makes the filtering of eventual network packet captures easy - so it became a habit.


Un-plug the adapter and restart systemd-networkd

sudo systemctl restart systemd-networkd

Plug the adapter back in:


Run

ip a 

and you should be able to locate the adapter.

10: rpilan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff permaddr 48:65:ee:1d:a7:51

The next step is to assign a static IP address to the dedicated adapter


Let's create a .network file for the adapter


sudo vim /etc/systemd/network/16-rpi-lan.network

With the following content:


[Match]
Name=rpilan0

[Network]
Address=10.1.1.1/24

Un-plug the adapter and restart systemd-networkd


sudo systemctl restart systemd-networkd

Plug the adapter and with the Pi connected and powered up so there is a signal

on the Ethernet cable


The output of

ip a 

should have an entry resembling this:


10: rpilan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff permaddr 48:65:ee:1d:a7:51
    inet 10.1.1.1/24 brd 10.1.1.255 scope global rpilan0
       valid_lft forever preferred_lft forever
    inet6 fe80::211:22ff:fe33:4455/64 scope link
       valid_lft forever preferred_lft forever

Once plugged-in the adapter will always be assigned the static address 10.1.1.1


Note: The numbered prefixes are used to define the order in which the configuration files are processed by systemd-networkd, more info in the manual page systemd.network(5)


Choosing and configuring a DHCP server.


There are a multitude of DHCP servers available, systemd-networkd has a builtin DHCP server.


Simply modify the previously created file /etc/systemd/network/16-rpi-lan.networkd add 'DHCPServer=yes' to the [Network] section


Edit the Uboot script and set the serverip variable to 10.1.1.1 and everything works fine.


However, I would prefer to use an external DHCP server mainly because I want to keep all configuration centralized in one place. This makes it easy to create a docker container, a chroot or a BSD jail later on. Network configuration varies from distribution to distribution so having control of the DHCP and DNS services is important.


I've decided to go with dnsmasq because it suits the use case as it made for small networks and is easy to configure https://thekelleys.org.uk/dnsmasq/doc.html


It is mainly a DNS forwarding and caching server but also handles DHCP/BOOTP


Install it like so:

apt-get install dnsmasq

Disable the systemd unit:


sudo systemctl disable dnsmasq 

A custom configuration will be provided and this also prevents it from being started by default on each boot


Note: libvirt makes use of dnsmasq and by default it listens on all interfaces

so check if there are no instances running like so:


sudo netstat -a -p | grep ^udp | grep domain

If there is no output it means that no other DNS server is running on the system


dnsmasq is configured by using command line arguments of which there are many, all explained in the manual page dnsmasq(8). A configuration file can also be used check /etc/dnsmasq.conf for examples


Here is the configuration file ~/src/rpi_boot/etc/dnsmasq.conf


interface=rpilan0
host-record=devhost,10.1.1.1
dhcp-range=10.1.1.10,10.1.1.20,12h
no-resolv
no-hosts
server=8.8.8.8

It only listens on the network adapter rpilan0, it can provide the address for the A record for devhost, the DHCP range has 10 available addresses. The DNS upstream server is google's DNS and reading /etc/resolv.conf is avoided as it contains the DNS server of the main network and so it requires to configure routing between the rpilan0 interface and the main network connection. This will be the focus of a subsequent article. The reading of /etc/hosts is also avoided for the same reason.


To test the configuration use the -d argument to prevent dnsmasq from detaching and

make it prints logs of DHCP transactions:


dnsmasq -d -C ~/src/rpi_boot/etc/dnsmasq.conf

Reboot the Pi - DHCP transactions should be logged


dnsmasq: started, version 2.90 cachesize 150
dnsmasq: compile time options: IPv6 GNU-getopt DBus no-UBus i18n IDN2 DHCP DHCPv6 no-Lua TFTP conntrack ipset no-nftset auth cryptohash DNSSEC loop-detect inotify dumpfile
dnsmasq-dhcp: DHCP, IP range 10.1.1.10 -- 10.1.1.20, lease time 12h
dnsmasq: using nameserver 8.8.8.8#53
dnsmasq: cleared cache
dnsmasq-dhcp: DHCPDISCOVER(rpilan0) b8:27:eb:7a:af:a3 
dnsmasq-dhcp: DHCPOFFER(rpilan0) 10.1.1.14 b8:27:eb:7a:af:a3 
dnsmasq-dhcp: DHCPREQUEST(rpilan0) 10.1.1.14 b8:27:eb:7a:af:a3 
dnsmasq-dhcp: DHCPACK(rpilan0) 10.1.1.14 b8:27:eb:7a:af:a3 
dnsmasq-dhcp: DHCPDISCOVER(rpilan0) b8:27:eb:7a:af:a3 
dnsmasq-dhcp: DHCPOFFER(rpilan0) 10.1.1.14 b8:27:eb:7a:af:a3 
dnsmasq-dhcp: DHCPREQUEST(rpilan0) 10.1.1.14 b8:27:eb:7a:af:a3 
dnsmasq-dhcp: DHCPACK(rpilan0) 10.1.1.14 b8:27:eb:7a:af:a3 

Make a test DNS request in another shell


dig @10.1.1.1 devhost

It should produce the following output


; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> @10.1.1.1 devhost
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42086
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;devhost.			IN	A

;; ANSWER SECTION:
devhost.		0	IN	A	10.1.1.1

;; Query time: 0 msec
;; SERVER: 10.1.1.1#53(10.1.1.1) (UDP)
;; WHEN: Sun May 25 11:39:17 CEST 2025
;; MSG SIZE  rcvd: 52

Re-configuring U-boot


Modify .config in ~/rpi_boot/dependencies/uboot/src/.config


Set CONFIG_CMD_DNS=y


Re-compile Uboot to add support for the dns monitor command


Update the U-Boot script like so:

setenv autoload 0
dhcp
dns devhost ${serverip}
wget ${kernel_addr_r} /kernel/linux5.10_rpi
booti ${kernel_addr_r} - ${fdt_addr}

Instead of using the setenv command with a hardcoded ip address, the dns command queries the ip address of the host 'devhost' and sets the environment variable used by the wget command to fetch the kernel.


Note: The kernel also needs to be re-compiled, change the root= argument to devhost in CONFIG_CMDLINE


The network infrastructure is now ready and in the next article we will focus on service management and creating scripts to automate the workflow.

14 Views
bottom of page