Should DHCP discover 0.0.0.0:68->255.255.255.255:67 be allowed in the Router?

Dear MT fans,
considering the Building Advanced Firewall in the VLAN context,
the rule below applied in firewall RAW will allow DHCP address assignation for devices from LAN (or VLANs with in-interface-list=VLAN)

/ip firewall raw
add action=accept chain=prerouting comment="defconf: accept DHCP discover" dst-address=255.255.255.255 dst-port=67 in-interface-list=LAN protocol=udp src-address=0.0.0.0 src-port=68

then traffic "in:Bridge out:(unknown 0) connection-state=new, proto=UDP, 0.0.0.0:68->255.255.255.255:67" is entering in the router via firewall FILTER due to Bridge is member of list=LAN

/ip firewall filter
add chain=input action=drop in-interface-list=!LAN

in my case VLANs are not part of list=LAN and also allowing traffic from add chain=input action=accept in-interface=Bridge , specific traffic logged above in not passing trough, probably due to
/interface bridge set frame-types=admit-only-vlan-tagged ...

so, should DHCP broadcast 0.0.0.0:68->255.255.255.255:67 need to entering in the router by allowing ingress (chain=input) from firewall Filter?

Thank you!

Some parts of the MikroTik documentation are not really reliable, and this RAW rule is an example of that.

The DHCP server and DHCP client in RouterOS (and in other OSes too) work with RAW sockets (you can do a web search with the DHCP raw sockets keywords for more literatures) and completely bypass the IPv4 firewall. This "RAW socket" is not related to the RAW firewall table!

You can change the rule above and change the action from action=accept to action=drop for example, while turning on logging, and would be able to observe that the clients have no problem obtaining DHCP leases from the router, while that rule is happily "dropping" packets.

In short, those rules, drop or accept, in the firewall (any table, not only RAW) are completely irrelevant and have no effects on the DHCP server or client in RouterOS.

Please note that on the IPv6 side it's different. DHCPv6 uses multicast and is affected by the firewall rules.

4 Likes

As @CGGXANNX mentioned the DHCP rule in the Mikrotik documentation is a poor example. I would also add - it creates more confusion.

Next, it is worth mentioning that accepting a packet in the /ip firewall raw table does not mean the packet is accepted. It literally means "stop processing this packet in the raw table, and send it to the connection tracker". The packet must be accepted (dropped or rejected) by some rule in your /ip firewall filter.

Now, to your question... You may need to add your VLAN interfaces as members of the LAN interface-list. Another reason that DHCP may not be passing through is most likely because this condition is too narrow:

dst-address=255.255.255.255 dst-port=67 src-address=0.0.0.0 src-port=68

It does accept DHCP discovery packets, but it will not match the DHCP renewal packets. When a host sends DHCP renewal request it already has a valid IP address assigned to it (during initial discovery stage), so the DHCP renewal packet not be matched by the above condition. Moreover, the DHCP renewal request does not use broadcast IP as its destination; instead it is sent to a (known) unicast IP address of the DHCP server.

The problem can be solved in two ways:

Option 1. Add another rule that specifically accepts DHCP renewal requests (in addition to discovery):

in-interface-list=LAN protocol=udp \
    dst-address=255.255.255.255 dst-port=67 src-address=0.0.0.0 src-port=68 \
    comment="accept DHCP discover" 

in-interface-list=LAN protocol=udp \
    dst-address-type=local dst-port=67 src-address-type=unicast src-port=68 \
    comment="accept DHCP renewals" 

OR

Option 2. Use a simple rule that captures both discovery AND renewal requests:
(Here, by omitting the 'src=address' and 'dst-address' we cover all DHCP scenarios: discovery and renewals. This is a preferred way of allowing DHCP traffic.)

  in-interface-list=LAN protocol=udp src-port=68 dst-port=67

As an example, below are DHCP rules that I use in my firewall:
(Note: The only reason I have these rules in the ‘/ip firewall raw’ table is to hide all DHCP traffic in the connection tracker. This it is done with action use ‘action=notrack’.)

# DHCP Server (LAN) - discovery and renewal requests from clients
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# (host 0.0.0.0 -> router 255.255.255.255) - IP discover
# (host x.x.x.x -> router x.y.z.a ) - IP renewal
/ip firewall raw
  add action=notrack chain=prerouting in-interface-list=LAN \
      protocol=udp src-port=68 dst-port=67 \
      comment="DHCP: [LAN->Router] Untrack discovery/renew requests"

# The *next* rule is accurate but invisible to RouterOS firewall; it can safely be removed. 
# Why: When a host requests a new IP address lease, the DHCP server must use Raw Sockets.
# This is a low-level mode to bypass standard IP firewall hooks and send packets to clients
# that do not have an IP address yet. When Mikrotik DHCP server sends a reply, it builds the
# packet and sends it directly to the network interface driver. Because the DHCP server
# operates at a low-level for these specific packets, it isn't "untracking" anything. The
# DHCP traffic is already bypassing the connection tracker because it is a Raw Socket process.
# As a result the packet counter stays at zero, so this rule can be removed.

# DHCP Server (LAN) - router's own replies
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ip firewall raw
add action=accept chain=output out-interface-list=LAN protocol=udp \
    src-port=67 dst-port=68 comment="DHCP: [Router->LAN] Untrack router's own reply"

The next two rules capture all DHCP traffic on the WAN port (discovery/renewal from your ISP):

# DHCP Client (WAN) - router's own discovery and renewal requests
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ip firewall raw
# (router 0.0.0.0 -> ISP 255.255.255.255) - IP discover
# (router x.x.x.x -> ISP a.b.c.d ) - IP renewal
 add action=notrack chain=output out-interface-list=WAN protocol=udp \
     src-port=68 dst-port=67 comment="DHCP: [Router->WAN] Untrack router's own DHCP discovery/renew requests"

# DHCP Client (WAN) - ISP reply
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
/ip firewall raw
# ( ISP a.b.c.d -> router 255.255.255.255) - ISP reply (discovery)
# ( ISP a.b.c.d -> router x.x.x.x) - ISP reply (renewal)
  add action=notrack chain=prerouting in-interface-list=WAN protocol=udp \
      src-port=67 dst-port=68 comment="DHCP: [WAN->Router] Untrack ISP reply"

And finally, I use a specific rule to explicitly accepts DHCP packets coming from LAN:

/ip firewall filter
add action=accept chain=input in-interface-list=LAN protocol=udp \
    src-port=68 dst-port=67 comment="Accept local DHCP IP discovery/renew"

P.S.

Since in your case VLANs are not part of list=LAN, what you can add your VLANs to a list.
For example:

/interface list member
   add list=ALL_LOCAL interface=vlan10_lan   comment="workstations"
   add list=ALL_LOCAL interface=vlan20_srv   comment="servers"
   add list=ALL_LOCAL interface=vlan30_iot   comment="IoT devices"
   add list=ALL_LOCAL interface=vlan50_guest comment="guests"

and then accept DHCP traffic from all your VLANs with this rule:

/ip firewall filter
    add action=accept chain=input in-interface-list=ALL_LOCAL protocol=udp \\
    src-port=68 dst-port=67 comment="Accept DHCP IP discovery/renew requests from all local subnets"

Hm, but what I meant in my previous post was:

Regardless of what you put in the firewall to handle the IPv4 DHCP related packets (drop them or allow them through in any table, apply NAT to them or not), all that have no influence on the working of DHCP in RouterOS. Both the IPv4 DHCP client and DHCP server in RouterOS are unaffected by whatever you do in the IP firewall.

So, the rule in the OP has only the effect that OP can see the counter on it increasing. Nothing changes with regard to DHCP handling when that rule is removed or turned into a drop rule.

It is correct, any rules for DHCP are NOT “needed” for DHCP to function, but I usually have those with logging option on, to have some extra registration of what happens on the network or sometimes in other environments to prevent a default “drop everything with logging” at the end of the chain to log the DHCP requests.

That is probably because of some "defconf: " rule(s) but I don't use them in my setup.
With regards to Raw Sockets sneaking past firewall, only the router's own replies use Raw Sockets. The Inbound DHCP discovery/renew packets are still subjects to firewall.

That is interesting statement. My observation is that only the router's own outgoing replies use Raw Sockets. The incoming DHCP discovery/renewals from clients are still subject to firewall rules, so I disagree with the above. In my past experience I did block myself from receiving the IP from the router via DHCP in the past by creating erroneous firewall rules.

... but again, I don't use the "defconf" rules. Perhaps it has something to do with the 'loopback' interface and the loopback 'accept' rule (whcih I don't have in my firewall) Both of these first appeared in RouterOS v7, and I only upgraded from v6 to v7 last month.

No. Firewall rules do not affect the DHCP server. Explained above.

So please explain how I was able to block myself from receiving IP via DHCP by screwing up my the firewall rules. It did happen in RouterOS v6.

please re-read my response above:

... only the router's own outgoing replies use Raw Sockets. The incoming DHCP discovery/renewals from clients are still subject to firewall rules

No, you can try adding rules that drop all those packets in all tables and chain that you want, and would be able to see that DHCP keeps working normally.

1 Like

Ok, here is a demo: Let's add these ultimate blocking rules at the top of RAW and Filter:

/ip firewall filter
add action=drop chain=input comment="DHCP test" dst-port=67 log=yes protocol=udp place-before=0
add action=drop chain=input comment="DHCP test" dst-port=68 log=yes protocol=udp place-before=0
add action=drop chain=input comment="DHCP test" log=yes protocol=udp src-port=67 place-before=0
add action=drop chain=input comment="DHCP test" log=yes protocol=udp src-port=68 place-before=0

/ip firewall filter
add action=drop chain=output comment="DHCP test" dst-port=67 log=yes protocol=udp place-before=0
add action=drop chain=output comment="DHCP test" dst-port=68 log=yes protocol=udp place-before=0
add action=drop chain=output comment="DHCP test" log=yes protocol=udp src-port=67 place-before=0
add action=drop chain=output comment="DHCP test" log=yes protocol=udp src-port=68 place-before=0

/ip firewall raw
add action=drop chain=prerouting comment="DHCP test" dst-port=67 log=yes protocol=udp place-before=0
add action=drop chain=prerouting comment="DHCP test" dst-port=68 log=yes protocol=udp place-before=0
add action=drop chain=prerouting comment="DHCP test" log=yes protocol=udp src-port=67 place-before=0
add action=drop chain=prerouting comment="DHCP test" log=yes protocol=udp src-port=68 place-before=0

And reboot the router. After reboot, router happily gets IP address via DHCP client on ether1:

Drop rule in RAW prerouting is hit twice.

Then I fired up a Windows test VM, which is the sole client of the test router:

Windows client get 192.168.88.254 assigned by the DHCP server on the router, although the drop rules counted packets:

On the filter table, all rules in chain input counts nothing, because packets are already dropped in RAW. A single rule in chain output counted some packets that it dropped, these correspond to packets sent by the DHCP client on the router:

But as we have seen above, that dropping has no effects because the DHCP client on ether1 had no problem obtaining IP address.

2 Likes

So I did run a quick test dropping inbound protocol=udp src-port=68 dst-port=67 from all local interfaces.

The firewall rule has no effect indeed.

What is interesting is that the rule gets hits, and the log shows the DHCP packets "dropped" (?) but DHCP clients continue getting their IPs.

Clearly this is Mikrotik circumvented their own system, which can best can be described as "a dirty hack". I can understand why this is done, however.

Here is the screenshot:

It is the same mechanism as is used by the “packet sniffer”. You can sniff any packet on the ethernet interface also when it is blocked in the firewall.

This is not MikroTik specific, it is a Linux feature and probably exists on many other systems.

What is not clear is how you were able to block it before, but that probably was a mis-diagnosis of another problem. It worked this way in v6 as well.

1 Like

Thank you, this was a good exercise and I did learn something new today.
Unless my memory fails me, RouterOS did not always act like this, as I did block myself in the past.
That was several years ago.

1 Like

who knows, it could have been was a mis-diagnosis of plain lack of understanding at that time. My first router had RouterOS v5.x. There were no "defconf" rules and I bricked it by excessive writes to nand flash. Learned a lot though :slight_smile:

@CGGXANNX

Can you elaborate more on DHCPv6 firewall rules?

The reason I ask.

I thought that 255.255.255.255 can be both broadcast and multicast and how it relates to DHCPv4.

255.255.255.255 and IPv6 are different worlds.

Anyway, I would not use that address in a firewall rule, but instead use the “address type” selector and choose “broadcast” and “multicast”.

I was referring to DHCPv4 and multicast.

Also, just remember what effects does firewall rules have when it comes to DHCP snooping if enabled.

On Linux some DHCP server implementations (not all) use raw socket hooks therefore bypassing firewall altogether and there is nothing dirty about it. Same is on RouterOS where not only DHCP but also MAC telnet and ping which are using udp 20561, on the other hand MTRDP (MT ROS neighbour discovery) does not and can be blocked by firewall.

The use of a raw socket is convenient on receive, because it allows to listen only on a specific interface (a “clean” socket listening for broadcasts would receive them from all interfaces, and a trick would still be required to get the interface info), but it is REQUIRED to have it on transmit, because the DHCP replies should be sent to the requestor which does not know its address yet. A “clean” socket would trigger an ARP exchange to derive the HW address from the IP address, which would not work.