Apparently that wasn't so trivial.
But I finally understand it.
I have read the manual "Packet Flow in RouterOS" (
Link) multiple time, every time understood a bit more.
My understanding is as follows:
The Bridge Filter doesn't know the VLAN interface name because that it is an early stage in the packet flow, it is before the Decapsulation phase.
When a packet reaches the "ROUTING DECISION" block, then necessarily it means that Decapsulation has executed, thus the VLAN interface name is available.
After the "Bridging Decision" block, a forwarded packet will go through the Forward chain (due to use-ip-firewall=yes), though this time without the extra information (like VLAN interface) which is available after a "ROUTING DECISION" block.
My solution:
I added an interface list of the "bridge-LAN" bridge ports which are relevant only to the LAN, i.e.:
ether1
ether2
ether3
ether4
Every virtual Wifi interface
The interface list is called "bridge_LAN_ports".
I excluded "ether5" because it is a WAN port.
I added a firewall rule which accepts a packet that complies with the criteria:
in-bridge-port-list=bridge_LAN_ports
out-bridge-port-list=bridge_LAN_ports
The firewall rule is added near the end of the IP Firewall forward chain, right before the rule which drops "ALL ELSE".
Until now, clients are still allowed to communicate in the same VLAN.
So we need to do the "vlan-client-isolation" feature.
I achieved this using Bridge Filter rules, as follows (an example):
/interface bridge filter
# The following VLANs don't have client isolation.
# (Meaning, clients of these VLAN may communicate with each other.)
add \
chain=forward \
action=accept \
mac-protocol=vlan \
vlan-id=### Insert here the VLAN ID for the LAN VLAN
comment="VLAN_LAN->self: Accept" \
log=yes log-prefix="bridge-forward-accept-VLAN_LAN-to-self:"
# ...
# The following VLANs have client isolation.
# (Meaning, clients of these VLAN cannot communicate with each other.)
add \
chain=forward \
action=drop \
mac-protocol=vlan \
vlan-id=### Insert here the VLAN ID for the Guest VLAN
comment="VLAN_GUEST->self: Drop" \
log=yes log-prefix="bridge-forward-drop-VLAN_GUEST-to-self:"
# ...
The reasoning behind this:
When a packet goes through the "BRIDGING" flow with use-ip-firewall=yes, then "necessarily" the packet will be executed against the IP Firewall forward chain (if "BRIDGING DECISION" decides it needs to be forwarded and the Bridge Filter doesn't block it).
So in that case, the
packet must be blocked only in the Bridge Filter (for it to be convenient). The packet cannot be conveniently blocked in IP Firewall because its VLAN interface isn't recognized.
Also, the fact that a packet reached the "BRIDGE FORWARD" block means that it necessarily needs to be forwarded "internally" in the bridge to some other port (it may be the same VLAN or some other VLAN).
So, the IP firewall rule which we added now performs the Accepting (of a packet) based on the bridge's port, which in our case is equivalent to "VLAN-to-self", because this is the only thing that is recognized during bridging.
BUT, when a packet is forwarded to another network, then it will go to "BRIDGE INPUT" and from there to "Decpasulation" which will reveal its VLAN interface name and then to IP Firewall.
Assuming that all packets in the network are VLANed properly, then these packets necessarily will have VLAN Interface name.
So, we could easily filter them using a VLAN interface name - I already had a IP firewall rule which drops packets from *any* VLAN interface to *any* VLAN interface - this provides the protection that a packet won't travel between distinct VLANs (unless specifically allowed).
Bottom line - IP firewall rules are something like this:
# ... <some stuff here>
{Drop a packet that match:
In interface list = VLAN_Interfaces
Out interface list = VLAN_Interfaces
}
{Accept a packet that match:
in-bridge-port-list=bridge_LAN_ports
out-bridge-port-list=bridge_LAN_ports
}
{Drop ALL ELSE}
And Bridge Filter is something like this:
{Accept a packet with VLAN-ID = <some_VLAN_ID_without_Client_Isolation>}
{Drop a packet with VLAN-ID = <some_VLAN_ID_with_Client_Isolation>}
Please let me know if you notice issues with this.