The Router Project — Part 5: DNS and DHCP with dnsmasq
Feb 13, 2018
9 minute read

This is part 5 of the router series; part 1, part 2, part 3 and part 4 are also available.

In the fifth part of this tutorial I am going to show how to configure dnsmasq to hand out IP addresses to your LAN clients as well as setting up dnsmasq to act as a DNS caching server. It basically combines dhcpd and bind (or unbind) into a single executable. Although the latter are obviously battle-tested for large networks in the context of a home office or a small business it is probably easier to start with dnsmasq.

The configuration of dnsmasq resides in a single file, usually /etc/dnsmasq.conf. What we want to achieve is basically three things

  1. Hand out IP addresses to the clients that use DHCP.
  2. Cache DNS queries for the LAN
  3. Assign dynamic DNS entries for the DHCP clients.

dnsmasq configuration: /etc/dnsmasq.conf

The absolute minimum configuration to do the above is

# Interface to listen to

# Default DNS servers

# Always expand the domain name

# The actual domain name

# Router advertisements for IPv6

# DHCP address allocation

# or if you have a dynamic IPv6 prefix it will be generated
# from the interface dnsmasq is bound to
# dhcp-range=::a:0,::a:ffff,constructor=eth1,ra-names,24h

# if you only want SLAAC then uncomment the following
# dhcp-range=::,slaac,ra-only

# Static DHCP configuration

The configuration is practically split into two things: DNS and DHCP. The most straightforward option is the interface that dnsmasq binds to and that is given through the interface=eth1 option, eth1 being the LAN-facing card. I will explain the rest options in the following sections. You might also want to add your domain name using the domain= option. If you don’t own one just make it up!

DNS caching

We will use dnsmasq as a kind of DNS caching proxy. Instead of having the clients resolve hostnames directly on the external DNS server we will have dnsmasq do the task. This takes advantage of caching and once a name has been resolved it will be done instantly the next time is requested again. The difference between 1 and 40 ms might not be significant but remember that web pages nowadays can do easily upwards of 60 DNS queries for a single page load (yes I lament that too, but oh well). In that case every bit counts. The upstream DNS servers are configured in the server= entries. In this case we are using the Google DNS but feel free to use anyone you like, for example your ISP’s. In contrast to the requirement of some C libraries that /etc/resolv.conf can hold a limited amount of nameserver entries there is no such restriction for dnsmasq.

In order to finalise the DNS caching configuration you must ensure that there is one and only one nameserver entry in the /etc/resolv.conf. This will ensure that all DNS queries made will pass through dnsmasq. If a hostname is cached it will be returned instantly; if not it will be forwarded upstream. You now only have to point the LAN members to use the IP address of your router as a DNS server. That is for the hosts that have static IP configurations. If you are using DHCP (up next!) dnsmasq will care of that!

And that’s it basically; you can now take advantage of the DNS caching feature of dnsmasq.

DHCP server: IPv4 edition

The reset of the configuration deals with DHCP and DHCPv6. The IPv4 case is really straighforward: through the dhcp-range option you just specify the initial address, final address, netmask and lease duration. For 90% of your IPv4 needs you are practically set. If you have more elaborate setup feel free to read the manpage (warning: although thorough, it’s huge). The nice thing is that dnsmasq will also generate fully qualified domain names (FQDNs) for your DHCP hosts if their hostname is known. For example if the computer with hostname foo connects through DHCP it will have the FQDN assigned. No need to remember these pesky numerical addresses. You also have the option of “pinning” some IP addresses to hosts if you know their MAC address using the dhcp-host option.

IPv6 address assignment: SLAAC and DHCPv6

At this point I would like to digress a bit and talk about automatic address assignment on IPv6. There are practically two ways to autoconfigure the IP addresses of the hosts. The first one is Stateless Autoconfiguration (SLAAC). In sort SLAAC allow hosts to configure themselves automatically without the use of a DHCP server. To do so the router has to send out Router Advertisements ie. let the hosts know some relevant details about the network the most important of which is the IPv6 prefix (which is assigned with DHCPv6-PD; see part 3) on the LAN-facing interface, eth1. Then through the builtin IPv6 stack on your operating system’s kernel an address will be generated automatically based on the MAC address of the host. That is unless you have configured the so called Privacy Extensions. In that case the actual address is randomised by hashing the MAC address and the current timestamp. Windows by default enable privacy extensions but on Linux (where they are called temporary addresses) it depends on a sysctl parameter: net.ipv6.conf.default.use_tempaddr. This parameter takes three possible values:

  • ≤0: No privacy extensions
  • =1: Enable but prefer a public address over the temporary
  • >1: Enable and prefer temporary addresses over any other

Basically if use_tempaddr is 1 privacy extensions will be enabled but if there is any other address available (remember interfaces can have many IPv6 addresses) it will be preferred. When use_tempaddr is >1 privacy extensions are always enforced and any other address is ignored when talking to the internet.

DHCPv6, on the other hand, is a bit more complicated. In a sense it works similarly to DHCP for IPv4 however there are two different modes of operation stateless DHCPv6 and stateful DHCPv6. In the first case address allocation is done through SLAAC, as described above, and DHCP is only used to convey additional network information. Personally, I do not find any specific advantage of stateless DHCPv6 over SLAAC so I have not investigated further.

Stateful DHCPv6 works similarly to its IPv4 counterpart. You can allocate a block of addresses similar to the IPv4 and they are handed out as you would expect. In an ideal world DHCPv6 should cover your dynamic address needs. However things are never that easy and there are some caveats to stateful DHCPv6 most important of which is that ANDROID DOES NOT SUPPORT IT at ALL in any version! You can read more about it in this -now legendary- bug report over at Android’s issue tracker.

In conclusion the only viable way to have DHCPv6 support in your network is to have both SLAAC and stateful DHCPv6 support in your gateway. In that case every host will be assigned two IPv6 addresses one through SLAAC and one through the DHCPv6 allocation pool. If you set use_tempaddr to be 0 or 2 in linux you will get to use that one instead of the SLAAC address, although your host will reachable by both. Devices that do not support DHCPv6 will only get the SLAAC address. Fortunately dnmsasq will generate DNS domain names for both of these cases.

So we have the following distinct cases for SLAAC/DHCPv6.

SLAAC only

You need the following options for this configuration.


SLAAC/Stateless DHCPv6

This case is a hybrid between SLAAC and DHCPv6. Address assignment will be done through SLAAC and dnsmasq will try to assign a domain name. This is a good compromise if you want only SLAAC but some degree of domain name assignment.


SLAAC and Stateful DHCPv6

Hosts will have two addresses, one from SLAAC and one from DHCPv6. dnsmasq will try to assign a domain name through the DHCP assigned address or if it’s not pingable through the autogenerated SLAAC address. Of course this will not work with privacy extensions.

# slaac is optional here as it has no additional effect

If you do not have a static IPv6 prefix dnsmasq can generate a range of addresses by using the prefix of your LAN-facing interface. If that is the case with your network replace the dhcp-range with the following

# again slaac is optional

Stateful DHCPv6 only

If you are not going to have any Android hosts on your network you can switch dnsmasq to stateful DHCPv6 operation. To do so just do not use any option in the dhcp-range line and remove enable-ra.


Finally there is the issue of static IPv6 assignment. You should be now aware that address pinning is not as straightforward with IPv6 as in the IPv4 case. It should be clear that is impossible to do so with SLAAC as the address is configured on the client side so this can only be achieved with DHCPv6. Once again things are not that straightforward. In contrast to DHCP there is no MAC address propagation when a client contacts the DHCPv6 server. There is, however, the so-called DHCP Unique Identifier (DUID) which is sent by the client to request an IPv6 address from the DHCPv6 server. The DUID is generated by the dhcp client so its value depends on what dhcp cliet you are using.

  • For dhclient it’s /etc/dhcp/dhclient6.conf
  • For dhcpcd it’s /etc/dhcpcd.duid
  • For dibbler it’s /var/lib/dibbler/client-duid
  • If you are using NetworkManager you will find it in /var/lib/NetworkManager/dhclient6. In contrast to all the other cases NetworkManager is using octal representation but you can decode it using the script found in this thread.
  • On Windows is found in the registry under HKLM > System > CurrentControlSet > services > TCPIP6 > Parameters although I never managed to make static DHCPv6 assignment work properly.

If you find your DUID you can use it similarly to how you use the MAC address for DHCP but don’t forget to prefix it with id:.


That’s pretty much it for DNS and DHCP. Although things are pretty straightforward with IPv4 the complexity of the many IPv6 layers make address assignment more complicated. If you want to keep things simple go just with SLAAC and don’t look back. In any case I strongly urge you to take a look at the dnsmasq manpage as there is a wealth of information to manage your network.

This concludes part 5. As usual if you are following this guide with Alpine linux don’t forget to commit your changes with lbu commit.