First, I'm aware that similar questions to mine have been asked elsewhere (I've read many of those posts!) but I've not been able to figure out a solution to my problem, so I'm asking for help.
My setup
I have two networks on my LAN, 192.168.0.0/24 and 10.11.12.0/24. The former has DHCP running on my WAN router, the latter runs dnsmasq on Ubuntu Server 20.04 on a box that also serves as a router between the two subnets. I've configured dnsmasq to only serve DHCP on the 10.11.12.0/24 network with the no-dhcp-interface=wlp3s0 option, wlp3s0 being the interface on the 192.168.0.0/24 side.
To summarise:
WAN Router (gateway to WAN) is 192.168.0.1/24
Runs DHCP for clients on 192.168.0.0/24
Router (b/w subnets):
Ubuntu Server 20.04 with two interfaces
wlp3s0: 192.168.0.2/24 (static IP)
eno1: 10.11.12.1/24 (static IP)
I've configured iptables to FWD and NAT like so.
iptables -t nat -A POSTROUTING -o wlp3s0 -j MASQUERADE
iptables -A FORWARD -i wlp3s0 -o eno1 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eno1 -o wlp3s0 -j ACCEPT
I've allowed DHCP through my firewall with ufw allow 67/udp
:
To Action From
-- ------ ----
67/udp ALLOW IN Anywhere
Everything appears to be working fine. However, from experience I know that running two DHCP servers like this can cause conflicts unless they are kept strictly separate. (Clients intended for the 192.168.0.0/24 network wouldn't be all too pleased if DHCP gave them an IP address on the 10.11.12.0/24 network!)
Discovering the problem
So I did the following to test it out. I set up a tcpdump listening on the wlp3s0 interface (192.168.0.2) and used nmap to simulate a DHCP Discover request originating on the eno1 interface (10.11.12.1/24), like so:
sudo tcpdump -i wlp3s0 -n udp port 67 or port 68
sudo nmap --script broadcast-dhcp-discover -e eno1
As expected I get a DHCP offer from dnsmasq running on 10.11.12.1. So far so good!
Some detail omitted from the output for brevity (he said)
Starting Nmap 7.80 ( https://nmap.org ) at 2021-12-22 16:19 UTC
Pre-scan script results:
| broadcast-dhcp-discover:
| Response 1 of 1:
| IP Offered: 10.11.12.53
| DHCP Message Type: DHCPOFFER
| Server Identifier: 10.11.12.1
| Broadcast Address: 10.11.12.255
| Subnet Mask: 255.255.255.0
| Domain Name Server: 10.11.12.1
|_ Router: 10.11.12.1
However, tcpdump shows that both DHCP servers respond to the discover request. (In fact, my WAN router sends 2 replies!)
16:19:43.517029 IP 10.11.12.1.68 > 255.255.255.255.67: BOOTP/DHCP, Request from de:ad:c0:de:ca:fe, length 316
16:19:46.486227 IP 10.11.12.1.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 321
16:19:46.794207 IP 192.168.0.1.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 323
16:19:46.794207 IP 192.168.0.1.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 323
It seems the DHCP discover request is forwarded to the 192.168.0.0/24 network via the wlp3s0 interface. Looking at my iptables and firewall rules I see no reason to be surprised by this. However, what I'm really struggling with is figuring out how to prevent it.
What I've tried
I've tried various permutations of rules in both ufw and iptables but to no avail, likely because I don't fully understand either iptables or ufw. A few examples of what I tried:
(All ufw/iptables rules were added at the top of the respective ruleset/chain to ensure they were hit.)
ufw:
# Restrict 67/udp to one interface
ufw allow in on eno1 proto udp from 0.0.0.0/0 to 0.0.0.0/0 port 67
# Deny routing
ufw route deny in on eno1 out on wlp3s0 to 0.0.0.0/0 port 67 proto udp
iptables:
# Prevent forwarding of UDP port 67/68
iptables -I FORWARD 1 -p udp --match multiport --dports 67,68 -j DROP
# Drop outbound UDP port 67
iptables -I OUTPUT 1 -p udp --match multiport --dports 67,68 -o wlp3s0 -j DROP
I must be missing some fundamental understanding of how this works since none of the above prevents the WAN router DHCP from receiving the discover and offering an IP on the 192.168.0.0/24 subnet.
I did read somewhere that DHCP servers may listen on a raw (or packet?) socket, which isn't subject to iptables. That isn't the case with dnsmasq as documented here https://github.com/imp/dnsmasq/blob/master/FAQ#L195. I also proved that by deleting the allow 67/udp rule in ufw, after which dnsmasq stops responding to DHCP discover. Perhaps surprisingly (or not), even with the allow rule deleted in ufw my WAN router continues to receive and respond to DHCP discover. Presumably because I have iptables rules configured to forward all traffic, but no equivalent INPUT rule (until I add one using ufw).
What I'd like to achieve
Preferably I'd like to prevent the DHCP discover broadcast on the 10.11.12.0/24 network from even reaching the 192.168.0.0/24 network.
If that proves difficult, I'd be happy to just prevent DHCP offers from my WAN router from reaching the 10.11.12.0/24 network.
Note that I do not have a problem on the 192.168.0.0/24 network since I've configured dnsmasq to not offer DHCP on the wlp3s0 interface (192.168.0.2). That appears to be working and DHCP discover on wlp3s0 (192.168.0.2/24) do not get a reply from dnsmasq.
So what am I missing? Am I going about this entirely wrong or am I just not getting it quite right? Any help is much appreciated.
Happy holidays!
Update
In response to a question in the comments from @sleepyhead, here is my iptables nat setup.
Chain PREROUTING (policy ACCEPT 13452 packets, 4312K bytes)
pkts bytes target prot opt in out source destination
Chain INPUT (policy ACCEPT 48 packets, 12715 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 87 packets, 10884 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 55 packets, 8622 bytes)
pkts bytes target prot opt in out source destination
32 2262 MASQUERADE all -- * wlp3s0 0.0.0.0/0 0.0.0.0/0
Update 2
Is it possible that this is an artefact of how I conduct my test? What makes me suspicious is the source IP of the tcpdump output. Could it be that running DHCP Discover on an interface that already has an IP produces a different result than if it didn't? Anyway, if I can repurpose one of my Raspberry Pis I'll run this test with it as a DHCP client and see if I get the same output, though I suspect I will.