9

On my other thread I was talking about some interesting things about the iptables policy and states, now I would like to understand more about how the DHCP works and how iptables understands it.

ETH0 is connected to my main switch that receives the dynamic ip from my router to gain not only internet access but aswell as access to my outer network.

ETH1 is the internal card that is connected to a internal switch where X clients receive their IPS from this server

ETH1 network is 192.168.1.0/255.255.255.0 where the server ip is 192.168.1.254.

From what I understood, dhcp is a bootp protocol so even if you have your firewall policies to DROP everything, your network would still receive the DHCP, which in the tests I made it seemed to be true.

From tcpdump:

root@test:~# tcpdump -i eth1 port 67 or 68
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
11:34:03.943928 IP 192.168.1.2.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 00:0c:29:29:52:8b (oui Unknown), length 303
11:34:03.957647 IP 192.168.1.254.bootps > 192.168.1.2.bootpc: BOOTP/DHCP, Reply, length 300
11:34:06.492153 IP 192.168.1.2.bootpc > 192.168.1.254.bootps: BOOTP/DHCP, Request from 00:0c:29:29:52:8b (oui Unknown), length 303
11:34:06.506593 IP 192.168.1.254.bootps > 192.168.1.2.bootpc: BOOTP/DHCP, Reply, length 300

I made a simple log rule to see what iptables does:

root@test:~# tail -f /var/log/syslog
Oct 15 11:30:58 test kernel: IN=eth1 OUT= MAC=ff:ff:ff:ff:ff:ff:00:0c:29:29:52:8b:08:00 SRC=192.168.1.2 DST=255.255.255.255 LEN=331 TOS=0x00 PREC=0x00 TTL=128 ID=9527 PROTO=UDP SPT=68 DPT=67 LEN=311
Oct 15 11:31:43 test kernel: IN=eth1 OUT= MAC=ff:ff:ff:ff:ff:ff:00:0c:29:29:52:8b:08:00 SRC=192.168.1.2 DST=255.255.255.255 LEN=331 TOS=0x00 PREC=0x00 TTL=128 ID=9529 PROTO=UDP SPT=68 DPT=67 LEN=311
Oct 15 11:33:32 test kernel: IN=eth1 OUT= MAC=ff:ff:ff:ff:ff:ff:00:0c:29:29:52:8b:08:00 SRC=192.168.1.2 DST=255.255.255.255 LEN=331 TOS=0x00 PREC=0x00 TTL=128 ID=9531 PROTO=UDP SPT=68 DPT=67 LEN=311
Oct 15 11:34:03 test kernel: IN=eth1 OUT= MAC=ff:ff:ff:ff:ff:ff:00:0c:29:29:52:8b:08:00 SRC=192.168.1.2 DST=255.255.255.255 LEN=331 TOS=0x00 PREC=0x00 TTL=128 ID=9533 PROTO=UDP SPT=68 DPT=67 LEN=311

Here is my iptables rules at the momment:

# deny all traffic
$IPT -P INPUT DROP
$IPT -P FORWARD DROP
$IPT -P OUTPUT DROP

# Use stateful inspection feature to only allow incoming connections
# related to connections I have already established myself
$IPT -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$IPT -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# allow all traffic on lo interface
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT

So even with the default POLICY to drop everything i still get the DHCP on my network while it does take a lot longer to renew an IP and such.

If I add the follow rule to my firewall:

$IPT -I OUTPUT -o $INTIF -p udp --dport 67:68 --sport 67:68 -j ACCEPT

It will take a LOT LESS TIME to update any client dhcp.

Considering the above:

  1. why does it indeed take longer time to update it even tought it is not being blocked ?
  2. is it possible to DROP the dhcp server at all without turning it off ?
  3. is it possible to ACCEPT the dhcp server within iptables with the BOOTP ? how is that done ?

If you know good links I wouldn't mind taking a lot :)

Guapo
  • 453
  • 2
  • 5
  • 13
  • I'm a bit confused by your tcpdump and iptables log output. Are you monitoring the network interface in promiscuous mode? Is your firewalled server actually using DHCP to configure its NIC? – Steven Monday Oct 15 '10 at 15:51
  • @Steven the server that has the firewall which is the one mentioned above has eth0 and eth1, eth0 receives dhcp as a client from the outside and eth1 is my dhcp server for my internal network which is the one i am talking about. What do u mean by `Are you monitoring the network interface in promiscuous mode` i am still learning... – Guapo Oct 15 '10 at 15:54
  • Oh, ok. You really need to better describe your network and servers if you want anyone to understand your question! – Steven Monday Oct 15 '10 at 16:01
  • About promiscous mode: http://en.wikipedia.org/wiki/Promiscuous_mode – Steven Monday Oct 15 '10 at 16:02
  • @Steven will do that now thanks for point out, do you think i have to specify anything else other then the network layout ? – Guapo Oct 15 '10 at 16:17
  • While i did found the wiki good and informational i will have to read and search more about it to understand how to verify wether it is or not in promiscuous mode, like i said, i just started learning about it :) a more detailed information about my network is at http://serverfault.com/questions/190454/how-to-make-client-c-and-b-receive-dhcp-from-client-a/190703#190703 – Guapo Oct 15 '10 at 16:28

3 Answers3

13

I'll answer #2: No.

When getting an IP address the dhcp daemon creates a raw socket to the network interface and handles the UDP protocol itself. Thus the UDP packets never go through iptables.

The reason the dhcp daemon has to implement UDP is that the kernel can only handle UDP (in fact all of the TCP/IP suite) when the interface has an IP address. Previously dhcp daemons would first give an interface the IP address of 0.0.0.0 but that no longer works.

Mark Wagner
  • 17,764
  • 2
  • 30
  • 47
  • 1
    *packet* socket. raw sockets *do* go through iptables. afaict. https://unix.stackexchange.com/a/447524/29483 – sourcejedi Jun 02 '18 at 20:16
5

Adding

$IPT -I INPUT -i $INTIF -p udp --dport 67:68 --sport 67:68 -j ACCEPT

shall make DHCPD update faster :) It shall works both side INPUT and OUTPUT. You can DROP dhcpd with ebtables, not with iptables. DHCPD listening at 0.0.0.0, not within IP

Ta Coen
  • 240
  • 4
  • 13
  • +1 I had it on INPUT and it was taking the same amount of time as if I didnt had it on my rules. When I made it OUTPUT it did make a HUGE difference. I will read about ebtables thanks. – Guapo Oct 15 '10 at 16:27
  • $IPT -I INPUT -i $INTIF -s 0/0 -p udp --dport 67:68 --sport 67:68 -j ACCEPT – Ta Coen Oct 15 '10 at 16:41
  • just tried the above and still taking too long compared to using the OUTPUT rule. – Guapo Oct 15 '10 at 16:45
  • Since the policy is DROP, then you shall use both INPUT and OUTPUT. – Ta Coen Oct 15 '10 at 16:55
5

My recent observation, on OpenWRT Kamikaze 7.09 = 2.4.34 and udhcpc from busybox 1.4.2 :

I have an "ACCEPT" policy in the OUTPUT chain, and in the INPUT direction, originally I relied on this classic catch-all rule:

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

to allow the DHCP responses in (to my udhcpc) on the WAN interface. I.e., this is where my ISP's upstream DHCP server assigns an IP Address to me.

Mind the difference between an initial DHCP exchange (discover, offer, request, ack) and a DHCP lease renewal (request, ack).

After boot, udhcpc starts by the full initial exchange. That exchange would succeed. And another renewal or two would succeed too - just a request and acknowledgement. My ISP's DHCP server typically asks for a renewal time of about an hour to 1.5 hours, thus my DHCP client asks for a renewal every 30 to 45 minutes (this behavior is based on the RFC).

But, on about the third or fourth renewal, it would start to get interesting. TCPdump would show about three or so renewal attempts, followed by a full initial exchange - that within a timespan of just a few minutes or even seconds. As if udhcpc didn't like what it got back :-( and would ultimately get satisfied with the full exchange. After that, another renewal in half an hour would succeed... and the story would repeat again.

I figured out, that it's perhaps the connection tracking in the kernel that's got something wrong. As if the conntrack entry expires after two hours or so, and the later DHCP renewals fail because the ACK from the server doesn't actually make it to udhcpc listening on the socket. Note that tcpdump (libpcap) listens on the raw interface and can see all packets coming in, before they're subject to iptables. Once udhcpc gives up renewals and, in despair, tries to start over from scratch using a full exchange (starting with DISCOVER), the kernel establishes a new conntrack entry and can understand related packets for some more time...

Sure enough, once I added something like:

iptables -A INPUT -i $OUT_IF -p udp --sport 67 --dport 68 -j ACCEPT

the renewals seem to work forever.

You may find the following tcpdump cmdline args useful:

tcpdump -vv -s 1500 -i eth0.1 port 67 or port 68

Note: the -vv asks for the verbose dissector output. eth0.1 is my WAN port (also a "NAT outside" interface).

An interesting attribute in the ACK packets is the LT: field = suggested / maximum granted lease time in seconds. DHCP requests are sent from port 68 to port 67. Responses come from port 67 to port 68.

techraf
  • 4,163
  • 8
  • 27
  • 44
frr
  • 261
  • 3
  • 6
  • Your story makes no sense. No matter if initial or renew, the DHCP exchange is always started by the client sending from port 68 to port 67, either to a specific IP or as broadcast. And with outgoing ACCEPT, this will always work. A reply from the outgoing destination should always pass through incoming by the ESTABLISHED rule, so renewal should work forever with just the ESTABLISHED rule as every renewal also renews or recreates the conntrack state. – Mecki Apr 19 '20 at 12:26
  • A renewal sends a request `openwrt:68 -> dhcpserver:67` and the ACK will be `dhcpserver:67 -> openwrt:68`. This counts as ESTABLISHED and will pass. If the old conntrack state expired, this establishes a new one. If there is a problem, it can only be with the initial exchange as the DISCOVER is `0.0.0.0:68 -> 255.255.255.255:67` and the OFFER will be `dhcpserver:67 -> new-openwrt:68`, which does not count as ESTABLISHED. This only works as DHCP usually bypasses iptables with a packet socket on Linux, otherwise an incoming rule is required that allows packets from UDP port 67 or to UDP port 68. – Mecki Apr 19 '20 at 12:33
  • 1
    Also you say that several renewals did succeed, yet every renewal that succeeds will also renew the conntrack state, so if your conntrack states expires after 2 hours, a renewal would extend it again by another 2 hours and so on and thus it will never expire. However, the default conntrack timeout for UDP is only 30 seconds and not 2 hours and I doubt that OpenWRT has changed that to 2 hours as that would be crazy. So your story seems totally flawed to me. – Mecki Apr 19 '20 at 12:52
  • @Mecki, thanks for the valuable feedback :-) By now, my response is 4 years old. About one year ago, my ASUS WL500G Deluxe was starting to get terminally flakey (after about 12 years of runtime, owing to capacitor replacements) and I chucked that old Kamikaze along with the hardware. Right now I'm running a more current OpenWRT on a modern TP-Link AP, and after some thought, I have not reused my old firewall scripts. OpenWRT now seems to have a good firewall out of the box... I mean to say that I cannot verify my past claims. All I know is that the extra iptables rule did help. – frr Apr 19 '20 at 21:02