I was too dump to open up the proper ports on my firewall before I started testing out my shiny new DHCP server, and it took a moment to dawn on me that it shouldn't work yet. I never opened port 67 on my server's firewall.
...
The simple answer is that DHCP is indeed special. To quote what a stranger quoted,
Per Mark Andrews of isc.org:
"DHCP uses packet filters and these tie into the IP stack before the firewall."
http://thr3ads.net/netfilter-buglog/2011/07/1961358-Bug-730-New-DHCP-request-and-other-traffic-bypasses-iptables-netfilter
-- https://www.centos.org/forums/viewtopic.php?t=8728
It's often stated that this is because the DHCP server uses raw sockets. I think this phrasing is quite confusing. Some official ISC docs for their DHCP server use "raw sockets" as a broad term, because it can run on a number of different platforms where it must use a number of different interfaces. On Linux, there is more than one type that you might hear referred to as raw sockets. Some are affected by Linux iptables, and some are not affected by Linux iptables.
I'm confident that Linux' TCP/IP stack imposes some restrictions when sending packets with PF_INET+SOCK_RAW. My vague memory was that DHCP on Linux does not necessarily work with that type of socket, and might need to use "packet sockets" instead. Packet sockets work at a lower level. I'm confident that packet sockets are not affected by iptables.
PF_PACKET sockets bypass the TCP/IP stack.
PF_INET/SOCK_RAW sockets still travers the TCP/IP stack.
-- https://lists.netfilter.org/pipermail/netfilter-devel/2003-March/010845.html
This quote was written in the context of receiving packets. There is also evidence that this applies to sending packets, as you might expect.
It seems that iptables is one of the restrictions that applies to the TCP/IP stack, including to sending with PF_INET+SOCK_RAW.
If I have a an IP datagram in userspace and I send it via a raw socket
created with socket(PF_INET, SOCK_RAW, IPPROTO_RAW) using the send()
system call, will this packet traverse the netfilter chains?
...
looks like good news:
ipt_hook: happy cracking.
ipt_hook: happy cracking.
ipt_hook: happy cracking.
ipt_tcpmss_target: bad length (10 bytes)
So your packets will traverse iptables.
https://lists.netfilter.org/pipermail/netfilter-devel/2003-March/010829.html
And the evidence for the receive direction:
It turns out that using raw sockets
gives me the packets post-NAT so the IP addresses are back in the
private range (10.x.x.x in my example). Maybe this is common knowledge
but I've struggled to find it documented. If I use libpcap/tcpdump I
get packets pre-NAT
[NAT is performed by iptables]
-- https://lists.gt.net/iptables/user/62529#62529
Bonus griping: I think the term "packet filter" in my initial quote is a straight out abuse, albeit a long-standing one. Berkeley Packet Filter is a mechanism used to install a filter on a raw socket, e.g. so that it only receives packets on the DHCP port. I think ISC at times refer to "Linux Packet Filter" as if it was a type of raw socket itself. It's not, and you can actually use BPF on normal UDP or TCP sockets.
sudo ufw status verbose
?tcpdump
, and then (in another window) rundhclient
.dhclient -v
and that said that it was indeed sending to the server on UDP port 67, so my theory that it could be trying port 68 as a fall back is probably wrong.