TTR Networks

Intermediate · DHCP and DNS on the Debian router · section 3 of 6

The firewall, and the first leases

✓ Lab-tested 5 November 2026 on Debian 13.7, dnsmasq 2.91, dhcpcd 10.1.0, nftables 1.1.3

dnsmasq is running, but the first staff PC to ask for an address gets nothing. dhcpcd sends its requests and times out.

The reason is the router's own firewall. DHCP requests and DNS queries are traffic to the router, so they pass through the input chain, which drops everything not explicitly allowed. The counter at the end of the chain shows the requests arriving and being dropped, and dnsmasq never sees them. (A client that waits long enough gives itself a 169.254.x.x address instead, IPv4 link-local addressing. That address is a reliable sign that no DHCP server answered.)

Two rules fix it: UDP 53 (DNS) and 67 (DHCP), and TCP 53 for DNS answers too big for UDP, from the three office VLANs only. Never from the WAN.

Leases

Now each VLAN gets an address from its own range:

  • The staff PC is offered 10.10.10.178, checks no one else is using it (dhcpcd's probing address step, an ARP check that takes a few seconds), and takes it for 12 hours, with the router as default gateway. Its /etc/resolv.conf now says nameserver 10.10.10.1 and domain office.lan.
  • The guest gets a 2-hour lease from the guest range.
  • The server gets 10.10.30.10, its reservation, not an address from the range.

The router keeps every lease in /var/lib/misc/dnsmasq.leases: expiry time, MAC address, IP address, name, and the client's ID. The names come from the clients themselves (dhcpcd sends the machine's hostname) or from the reservation, which wins.

The first attempt (real output)
root@staff:~# dhcpcd -4 -1 -t 10 eth0.10; ip -br addr show eth0.10
dhcpcd-10.1.0 starting
DUID 00:01:00:01:32:7e:68:88:02:00:00:00:10:20
eth0.10: IAID ff:00:00:0a
eth0.10: soliciting a DHCP lease
eth0.10: probing for an IPv4LL address
timed out
dhcpcd exited
eth0.10@eth0     UP             

root@router:~# nft list chain inet filter input | grep everything
		counter packets 2 bytes 656 comment "everything else to the router"
  • Lines 5–8: The request goes out, nothing comes back.
  • Lines 11–12: The router's input chain counted and dropped the requests: dnsmasq never saw them.
/etc/nftables.conf: the input chain
#!/usr/sbin/nft -f
# /etc/nftables.conf on the office router.
# Check, then load:  nft -c -f /etc/nftables.conf && systemctl reload nftables
flush ruleset

define WAN = "eth0"

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    ct state invalid drop
    iif lo accept
    icmp type { echo-request, destination-unreachable, time-exceeded } accept
    iifname "staff" tcp dport 22 accept comment "SSH from staff only"
    iifname { "staff", "guest", "servers" } udp dport { 53, 67 } accept comment "DNS and DHCP for the office"
    iifname { "staff", "guest", "servers" } tcp dport 53 accept comment "DNS over TCP, for large answers"
    counter comment "everything else to the router"
  }

  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state established,related accept
    ct state invalid drop
    # Who may start connections to whom: one line per allowed pair.
    iifname . oifname vmap {
      "staff"   . $WAN      : accept,
      "staff"   . "servers" : accept,
      "guest"   . $WAN      : accept,
      "servers" . $WAN      : accept,
    }
    counter comment "every other pair is dropped"
  }
}

table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat;
    oifname $WAN masquerade comment "the office shares the router's public address"
  }
}
  • Line 16: DNS and DHCP, from the office VLANs only.
  • Line 17: DNS falls back to TCP for large answers (DNSSEC, many records).
Leases on every VLAN (real output)
root@router:~# nft -c -f /etc/nftables.conf && systemctl reload nftables && nft list chain inet filter input | grep -E "DNS"
		iifname { "staff", "guest", "servers" } udp dport { 53, 67 } accept comment "DNS and DHCP for the office"
		iifname { "staff", "guest", "servers" } tcp dport 53 accept comment "DNS over TCP, for large answers"

root@staff:~# dhcpcd -4 -1 -t 30 eth0.10; ip -br addr show eth0.10; ip route; grep -v "^#" /etc/resolv.conf
dhcpcd-10.1.0 starting
DUID 00:01:00:01:32:7e:68:88:02:00:00:00:10:20
eth0.10: IAID ff:00:00:0a
eth0.10: soliciting a DHCP lease
eth0.10: offered 10.10.10.178 from 10.10.10.1
eth0.10: probing address 10.10.10.178/24
eth0.10: leased 10.10.10.178 for 43200 seconds
eth0.10: adding route to 10.10.10.0/24
eth0.10: adding default route via 10.10.10.1
exiting due to oneshot
dhcpcd exited
eth0.10@eth0     UP             10.10.10.178/24 
default via 10.10.10.1 dev eth0.10 proto dhcp src 10.10.10.178 metric 1012 
10.10.10.0/24 dev eth0.10 proto dhcp scope link src 10.10.10.178 metric 1012 
domain office.lan
nameserver 10.10.10.1

root@guest:~# dhcpcd -4 -1 -t 30 eth0.20 2>&1 | grep -E "leased|adding default"; ip -br addr show eth0.20
eth0.20: leased 10.10.20.162 for 7200 seconds
eth0.20: adding default route via 10.10.20.1
eth0.20@eth0     UP             10.10.20.162/24 

root@server:~# dhcpcd -4 -1 -t 30 eth0.30 2>&1 | grep -E "leased|adding default"; ip -br addr show eth0.30
eth0.30: leased 10.10.30.10 for 43200 seconds
eth0.30: adding default route via 10.10.30.1
eth0.30@eth0     UP             10.10.30.10/24 

root@router:~# cat /var/lib/misc/dnsmasq.leases
1793873197 02:00:00:00:30:10 10.10.30.10 fileserver ff:ff:00:00:1e:00:01:00:01:32:7e:68:cf:02:00:00:00:30:10
1793837191 02:00:00:00:20:20 10.10.20.162 guest ff:ff:00:00:14:00:01:00:01:32:7e:68:c5:02:00:00:00:20:20
1793873186 02:00:00:00:10:20 10.10.10.178 staff ff:ff:00:00:0a:00:01:00:01:32:7e:68:88:02:00:00:00:10:20
  • Lines 1–3: The new rules, checked and loaded.
  • Lines 10–12: Offered, checked with an ARP probe, and leased for 12 hours.
  • Lines 17–19: An address and a default route via the router.
  • Lines 20–21: DNS: the router, and office.lan as the search domain.
  • Line 24: Guests: 7200 seconds, the 2-hour lease.
  • Line 29: The file server gets its reservation, not a pool address.
  • Lines 34–36: The router's lease file: expiry, MAC, IP, name and client ID.

Check your understanding

A guest's laptop shows the address 169.254.12.7. What does that tell you?

  • No DHCP server answered it; it gave itself a link-local address.
  • dnsmasq handed out an address from the wrong range.
  • The guest VLAN uses a different subnet on purpose.
  • DNS is broken, but DHCP worked.