Table of content
  1. DNS
  2. DHCP
    1. Address allocation
    2. Diskless booting
  3. TFTP

We will use dnsmasq to provide basic networking (DNS forwarding, DHCP configuration, PXE booting) to a dedicated network behind a NAT. In this example the gateway to the network is done through the interface vmnet0 which is configured as 192.168.100.1/24.

As our system is already running a local DNS resolver (local_unbound) we need to ensure that dnsmasq doesn’t steal its requests by listening on the wildcard address (0.0.0.0 or ::), so we explicitly bind to the desired interfaces.

dnsmasq listen on the loopback interface even if not requested in interface directive so it needs to be explicitly removed using except-interface

dnsmasq.conf
1
2
3
4
# Select listening interface
interface=vmnet0        # Select the interface on which to listen
except-interface=lo0    # Ensure we don't listen on loopback 
bind-interfaces         # Bind to interfaces instead of listening on wildcard

During the setup phase in can be useful to log information about the DNS queries handled or the DHCP messages exchanged.

dnsmasq.conf
1
2
3
# Extra logging (for debugging)
log-queries             # Log DNS queries
log-dhcp                # Log information about DHCP exchanges

DNS

Here we are using the default behaviour of dnsmasq which is to:

  • read /etc/resolv.conf to identify name servers to use for resolving queries
  • read /etc/hosts to make locally defined host available for name resolution

To try to avoid forwarding name resolution up to the DNS root servers, only the requests having a domain part (ie: names with a dot in it) will be forwarded. Other restrictions are possibles such as bogus-priv, which forbid private addresses reverse lookups (like: 192.168.x.y) but they will not be enabled as in our configuration we are on a private network if it’s not your case you should consider enabling it.

dnsmasq.conf
1
2
domain-needed           # Don't forward plain name (without dot) resolution 
#bogus-priv             # Recommanded if not on a private network

We enable DNSSEC validation to ensure authenticity of name resolution. But as lot of domains don’t sign their zones we don’t want to have name resolution for these domains rejected, so extra check will be performed to distinguish unsigned domain from forgery (dnssec-check-unsigned).

dnsmasq.conf
1
2
3
4
# Enabling DNSSEC
conf-file=/usr/local/share/dnsmasq/trust-anchors.conf
dnssec                  # Enable DNSSEC
dnssec-check-unsigned   # Check if unsigned answer is from unsigned domain

DHCP

Usually you should never run dnsmasq on your main interface (even worst if dhcp-authoritative is set), failing to do this will summon a really angry sysadmin, as you will serve bogus answer on its network.

As the purpose of dnsmasq is to act as a DNS + DHCP + TFTP server, the corresponding options (router (3), dns-server (6), and next server) in DHCP answers will be filled automatically to announce the IP address on which dnsmasq is listening, so you shouldn’t need to specify them. Nevertheless if required it is possible to overwrite defaults (replacing 192.168.100.1 by the desired address) using:

1
2
3
dhcp-option=option:router, 192.168.100.1             # Set default gateway
dhcp-option=option:dns-server, 192.168.100.1         # Set DNS servers to announce
dhcp-boot=bootfile, next_servername, 192.168.100.1   # Set next server

It is possible to have conditional configuration for numerous directives (dhcp-option, dhcp-host, dhcp-boot, … ) by using tag:my-tag, documentation for each directive must be read to know the exact placement of the tag keyword.

Address allocation

We define our DHCP server as authoritative (we are the only DHCP server on our network), and the range of IP addresses that can be dynamically allocated (with it default lease time).

dnsmasq.conf
1
2
dhcp-authoritative         # Consider we are the only DHCP server on the network
dhcp-range=192.168.100.10,192.168.100.150,24h  # Range with a 24 hour lease time
dnsmasq.conf
1
2
dhcp-host=debian,192.168.100.55,infinite
#dhcp-host=58:9c:fc:0e:be:60,192.168.100.50,infinite

Diskless booting

We will create various tag for conditional branching, they will be mainly based on the vendor class, the client architecture, and the MAC address (which prefix identify the card manufacturer).

The number1 defined in the client architecture option allows to identify the computer architecture (i386, x86_64, arm32, arm64, …), it’s interface (BIOS, UEFI, …), and the type of client (default or http). The following table give a summary of it:

Id Architecture Interface Client Deprecated
0 i386 BIOS    
1 NEC/PC98    
2 Itanium      
3 DEC Alpha    
4 Arc x86    
5 Intel Lean Client    
6 x86_64 UEFI    
7 x86_64 UEFI    
8 Xscale EFI  
9 EBC UEFI    
10 ARM 32-bit UEFI    
11 ARM 64-bit UEFI    
12 PowerPC Open Firmware    
13 PowerPC ePAPR    
14 PowerPC OPAL v3    
15 x86_64 UEFI http  
16 i386 UEFI http  
17 EBC UEFI http  
18 ARM 32-bit UEFI http  
19 ARM 64-bit UEFI http  
20 i386 BIOS http  
21 ARM 32-bit uboot    
22 ARM 64-bit uboot    
23 ARM 32-bit uboot http  
24 ARM 64-bit uboot http  
25 RISC-V 32-bit UEFI    
26 RISC-V 32-bit UEFI http  
27 RISC-V 64-bit UEFI    
28 RISC-V 64-bit UEFI http  
29 RISC-V 128-bit UEFI    
30 RISC-V 128-bit UEFI http  
31 s390 Basic      
32 s390 Extended      
33 MIPS 32-bit UEFI    
34 MIPS 64-bit UEFI    
35 Sunway 32-bit UEFI    
36 Sunway 64-bit UEFI    

Base on this table, we define several tags corresponding to (this can of course be reduced to the list you really need):

We also define tags based on:

Force HTTPClient option in response, if it was set in the request:

dnsmasq.conf
1
dhcp-option-force=tag:http,60,HTTPClient

Load the preferred client (here: iPXE) to manage the boot process:

dnsmasq.conf
1
2
3
4
5
6
7
8
9
# By default load the iPXE implementation
# - from PXEClient
dhcp-boot=tag:pxe, tag:uefi,tag:i386,  ipxe.efi-i386
dhcp-boot=tag:pxe, tag:uefi,tag:x86_64,ipxe.efi-x86_64
dhcp-boot=tag:pxe, tag:bios,           ipxe.pxe
# - from HTTPClient
dhcp-boot=tag:http,tag:uefi,tag:i386,  http://boot.example.com/ipxe.efi-i386
dhcp-boot=tag:http,tag:uefi,tag:x86_64,http://boot.example.com/ipxe.efi-x86_64
dhcp-boot=tag:http,tag:bios,           http://boot.example.com/ipxe.pxe

Now that we are running the desired client, we need to choose how to boot (the examples below are snippets that need to be customize and prefixed with additional tags):

TFTP

The TFTP protocol can be used if you need to retrieve files during the boot process (kernel, bootloader, configuration, …), and dnsmasq already embed such a server.

TFTP is not the most secure protocol or the one with the best transfer rate. Recent PXE/UEFI implementation allow the use of HTTP or HTTPS instead.

dnsmasq.conf
1
2
3
enable-tftp               # Enable tftp
tftp-root=/var/tftp/      # Directory used for serving files
tftp-secure               # Only serve file if it is owned by dnsmasq user