DNSMasq with a Raspberry Pi DNS, DHCP, and Static Leases

Assumptions

  • You’re running Raspbian or a Debian-based distro on your Pi. Although there should be little difference, if any, in most areas between instances on other Operating Systems.
  • You know how to navigate the Linux – editing files, cron…

DNSMasq Configuration File

The below DNSMasq configuration was mostly copied, with some changes, from here. Extra verbose comments are either my comments, or ones taken from what I found pertinent on this man page.

########################################
# DHCP
########################################

## man page for this config file: http://oss.segetech.com/intra/srv/dnsmasq.conf
## See current leases given here: /var/lib/misc/dnsmasq.leases

# Add domains which you want to force to an IP address here. The example below send any host in doubleclick.net to a local webserver.
address=/doubleclick.net/127.0.0.1

# Add the domain to simple names (without a period) in /etc/hosts in the same way as for DHCP-derived names. Note that this does not apply to domain names in cnames, PTR records, TXT records etc.
expand-hosts

# Range of IPs to use for DHCP Pool. Can have multiple ranges. Last part is lease time.
# Enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
dhcp-range=10.0.0.100,10.0.0.200,48h
dhcp-option=6,10.0.0.33,10.0.0.1
# Override the default route supplied by dnsmasq, which assumes the router is the same machine as the one running dnsmasq.
dhcp-option=option:router,10.0.0.1
# Set the NTP time server addresses
dhcp-option=option:ntp-server,10.0.0.33

# Send microsoft-specific option to tell windows to release the DHCP lease when it shuts down. Note the "i" flag, to tell dnsmasq to send the value as a four-byte integer - that's what microsoft wants. See http://technet2.microsoft.com/WindowsServer/en/library/a70f1bb7-d2d4-49f0-96d6-4b7414ecfaae1033.mspx?mfr=true
dhcp-option=vendor:MSFT,2,1i

########################################
# STATIC LEASES
# There are many ways to assign these leases. See the man page for the .conf
########################################
# System
dhcp-host=AA:BB:CC:DD:EE,router,10.0.0.1,infinite

# Human 1
dhcp-host=00:11:22:33:44,ps3,10.0.0.24,48h
dhcp-host=01:11:22:33:44,ps4,10.0.0.25,48h

########################################
# DNS
########################################
# Listen on this network interface
interface=eth0
# The IP address of the Raspberry Pi
listen-address=10.0.0.33,127.0.0.1

# For debugging purposes, log each DNS query as it passes through dnsmasq.
# log-queries

# Tells dnsmasq to never forward A or AAAA queries for plain names, without dots or domain parts, to upstream nameservers. If the name is not known from /etc/hosts or DHCP then a "not found" answer is returned.
domain-needed

# Set the domain for dnsmasq. this is optional, but if it is set, it
# does the following things.
# 1) Allows DHCP hosts to have fully qualified domain names, as long
#     as the domain part matches this setting.
# 2) Sets the "domain" DHCP option thereby potentially setting the
#    domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"
domain=home.local

# Add local-only domains here, queries in these domains are answered from /etc/hosts or DHCP only.
local=/home.local/

# Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc) which are not found in /etc/hosts or the DHCP leases file are answered with "no such domain" rather than being forwarded upstream.
bogus-priv

# Don't read /etc/resolv.conf. Get upstream servers only from the command line or the dnsmasq configuration file.
no-resolv

# Don't poll /etc/resolv.conf for changes.
no-poll

# Don't read the hostnames in /etc/hosts.
no-hosts

# Uses this file as a hosts file. Useful for simple DNS lookups.
addn-hosts=/etc/dnsmasq_static_hosts.conf

# If this is commented out, when resolving DNS for local systems, you'd have to append what is defined in the 'local=' option
expand-hosts

# Size of local DNS cache. Units=sites. cache-size=10000 means it'll cache 10,000 DNS names.
cache-size=10000

# Specify IP address of upstream servers directly.
# DNSMasq does DNS lookups against these public DNS servers
server=8.8.3.3  # Google DNS
server=8.8.8.8  # Google DNS

Updating DNS with Cron

For whatever reason, doing DNS queries against my Raspberry Pi with the host names defined in in /etc/dnsmasq.conf this way, binding the name to the MAC Address as I want, would fail. Queries would only return with an A record if I had put the IPs/Hostnames in the path with the addn-hosts option.

I didn’t want to have to define hosts in two different files. Without an obvious solution, I decided to set up a cron job to update /etc/dnsmasq_static_hosts.conf on a daily basis, taking just the static leases I wanted and formatting them just as would be done in /etc/hosts.

sudo crontab -e
0 4 * * * * cat /etc/dnsmasq.conf | grep dhcp-host | tr ',' ' ' | awk '{print$3, $2}' > /etc/dnsmasq_static_hosts.conf && dnsmasq --test; if [ $? -eq 0 ]; then systemctl restart dnsmasq; else echo "Error with DNSMasq Config"; fi

This cronjob will run at 4am and:

  1. cat: read out the DNSMasq configuration
  2. grep: locate only the lines for static leases
  3. tr: convert commas into spaces (as the option parameters are formatted like a CSV)
  4. awk: Extract the hostname and IP address and print only them in reverse order
  5. >: Overwrite the DNSMasq configuration file
  6. dnsmasq –test: Sees if the formatting of the config file is acceptable 1. If the syntax is correct, then restart the daemon
  7. Otherwise it has a syntax issue, don’t restart it. 1. (The echo isn’t needed for cron as it won’t be seen normally. I left it in for copying/pasting/testing)

What it writes to the file where you can resolve to your static leases by hostname: 10.0.0.11 router 10.0.0.24 ps3 10.0.0.25 ps4

Bash Alias to see current DHCP leases

  1. Edit your user’s .bashrc:
    cd ~
    sudo nano .bashrc
    
  2. Add the alias to print MAC / IP INFO (If I remember correctly)
    `alias leases="cat /var/lib/misc/dnsmasq.leases | awk '{print \$3, \$4, \$2}' | sort -t . -k 3,3n -k 4,4n"`
    

Logging

DHCP logs should look like:

tail -f /var/log/daemon.log Dec 30 03:18:58 raspberrypi dnsmasq-dhcp[5265]: DHCPDISCOVER(eth0) 10.0.0.24 0c:fe:45:54:69:36 Dec 30 03:18:58 raspberrypi dnsmasq-dhcp[5265]: DHCPOFFER(eth0) 10.0.0.24 0c:fe:45:54:69:36 Dec 30 03:18:58 raspberrypi dnsmasq-dhcp[5265]: DHCPREQUEST(eth0) 10.0.0.24 0c:fe:45:54:69:36 Dec 30 03:18:58 raspberrypi dnsmasq-dhcp[5265]: DHCPACK(eth0) 10.0.0.24 0c:fe:45:54:69:36 d-ps3

DNS logs should look like:

Dec 30 02:52:27 raspberrypi dnsmasq[23479]: query[A] ssum-sec.casalemedia.com from 10.0.0.185 Dec 30 02:52:27 raspberrypi dnsmasq[23479]: cached ssum-sec.casalemedia.com is <CNAME> Dec 30 02:52:27 raspberrypi dnsmasq[23479]: forwarded ssum-sec.casalemedia.com to 8.8.8.8 Dec 30 02:52:27 raspberrypi dnsmasq[23479]: query[A] ums.adtechus.com from 10.0.0.185 Dec 30 02:52:27 raspberrypi dnsmasq[23479]: forwarded ums.adtechus.com to 8.8.8.8

Problems

  • DHCP relay with guest network.

Further/Alternate Reading

This is a preview of Clap Button, a new feedback and analytics tools for Hydejack, built by yours truly. You can try it out on localhost for free, but it will be removed (together with this message) when building with JEKYLL_ENV=production. To use Clap Button on your site, get a subscription
and set clap_button: true in your config file.


© 2021. All rights reserved.