Skip to content

Firewall and QoS Case Studies

This section provides practical case studies demonstrating how to build comprehensive firewall configurations and implement Quality of Service (QoS) traffic prioritization on MikroTik routers running RouterOS. These case studies cover real-world scenarios including advanced firewall building with RAW filtering, protecting both the router and connected clients, and using connection rate-based traffic detection for intelligent bandwidth management.

The examples illustrate proper usage of firewall features including interface-lists, address-lists, RAW chains, connection tracking, and mangle rules for traffic classification. Each case study presents complete configuration examples that can be adapted to specific network environments while following security best practices and RFC recommendations.

This case study demonstrates how to build a comprehensive firewall using multiple firewall features together. The approach uses RAW firewall for high-performance early packet filtering, with regular firewall rules handling only essential filtering tasks. This architecture maximizes router performance while maintaining strong security posture.

Building an advanced firewall requires combining multiple RouterOS firewall features in a layered approach. RAW firewall chains process packets before connection tracking, allowing early dropping of unwanted traffic without consuming connection tracking resources. Regular firewall filter chains handle stateful inspection for legitimate traffic, while NAT rules provide address translation for outbound connections.

The configuration separates IPv4 and IPv6 handling, applies interface-lists for easier management, and implements defense-in-depth principles with multiple validation layers. Most traffic filtering occurs in RAW chains, with the regular firewall containing minimal rules for established/related connections and final access control.

Interface-lists provide a centralized way to manage interface groupings. Create WAN and LAN lists to simplify firewall rule maintenance across multiple interfaces.

/interface list
add comment=defconf name=WAN
add comment=defconf name=LAN
/interface list member
add comment=defconf interface=bridge list=LAN
add comment=defconf interface=ether1 list=WAN

Add all internet-facing interfaces to the WAN list, such as ether1, pppoe-out, or wireless interfaces providing uplink connectivity. Add all internal network interfaces to the LAN list, including bridge interfaces representing internal networks. This abstraction allows firewall rules to reference in-interface-list=LAN rather than individual interface names, simplifying future changes when adding new network segments.

The input chain controls traffic destined for the router itself. Default policy should deny all access except explicitly permitted traffic from trusted networks.

IPv4 Input Rules:

/ip firewall filter
add action=accept chain=input comment="defconf: accept ICMP after RAW" protocol=icmp
add action=accept chain=input comment="defconf: accept established,related,untracked" connection-state=established,related,untracked
add action=drop chain=input comment="defconf: drop all not coming from LAN" in-interface-list=!LAN

IPv6 Input Rules:

/ipv6 firewall filter
add action=accept chain=input comment="defconf: accept ICMPv6 after RAW" protocol=icmpv6
add action=accept chain=input comment="defconf: accept established,related,untracked" connection-state=established,related,untracked
add action=accept chain=input comment="defconf: accept UDP traceroute" dst-port=33434-33534 protocol=udp
add action=accept chain=input comment="defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=udp src-address=fe80::/10
add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 protocol=udp
add action=accept chain=input comment="defconf: accept IPsec AH" protocol=ipsec-ah
add action=accept chain=input comment="defconf: accept IPsec ESP" protocol=ipsec-esp
add action=drop chain=input comment="defconf: drop all not coming from LAN" in-interface-list=!LAN

The ICMPv6 rules include UDP traceroute ports, DHCPv6 prefix delegation, and IPsec protocols as recommended by RFC 4890 for proper IPv6 network operation. When using DHCPv6 relay, adjust the source address filter to accept relay addresses instead of link-local addresses.

Client protection focuses on preventing unauthorized access attempts from the internet and blocking traffic to/from non-routable address ranges. Create address-lists containing addresses that should never be forwarded.

IPv4 Block Lists:

/ip firewall address-list
add address=0.0.0.0/8 comment="defconf: RFC6890" list=no_forward_ipv4
add address=169.254.0.0/16 comment="defconf: RFC6890" list=no_forward_ipv4
add address=224.0.0.0/4 comment="defconf: multicast" list=no_forward_ipv4
add address=255.255.255.255/32 comment="defconf: RFC6890" list=no_forward_ipv4

IPv6 Block Lists:

/ipv6 firewall address-list
add address=fe80::/10 comment="defconf: RFC6890 Linked-Scoped Unicast" list=no_forward_ipv6
add address=ff00::/8 comment="defconf: multicast" list=no_forward_ipv6

Disable multicast entries if multicast forwarding is required in the network.

Forward Chain Rules:

/ip firewall filter
add action=accept chain=forward comment="defconf: accept all that matches IPsec policy" ipsec-policy=in,ipsec disabled=yes
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" connection-state=established,related
add action=accept chain=forward comment="defconf: accept established,related, untracked" connection-state=established,related,untracked
add action=drop chain=forward comment="defconf: drop invalid" connection-state=invalid
add action=drop chain=forward comment="defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat connection-state=new in-interface-list=WAN
add action=drop chain=forward src-address-list=no_forward_ipv4 comment="defconf: drop bad forward IPs"
add action=drop chain=forward dst-address-list=no_forward_ipv4 comment="defconf: drop bad forward IPs"

The rule dropping new connections from WAN that are not DSTNATed provides protection against direct attacks on internal clients. Even if RAW rules are misconfigured, this double-layer protection prevents unauthorized access attempts from reaching internal network addresses.

Enable NAT for devices behind the router to access the internet. Use masquerade for dynamic WAN addresses or src-nat for static addresses.

/ip firewall nat
add action=accept chain=srcnat comment="defconf: accept all that matches IPsec policy" ipsec-policy=out,ipsec disabled=yes
add action=masquerade chain=srcnat comment="defconf: masquerade" out-interface-list=WAN

Enable the IPsec policy matcher rule when using IPsec tunnels to prevent NAT from modifying encrypted IPsec traffic. This rule is disabled by default and must be explicitly enabled when IPsec is deployed.

RAW chains provide high-performance packet filtering before connection tracking. Use RAW rules to drop unwanted traffic early, reducing CPU load on the regular firewall and connection tracking table.

Create address-lists identifying addresses that should be dropped immediately upon detection.

Bogon IPs (Never valid on public internet):

/ip firewall address-list
add address=127.0.0.0/8 comment="defconf: RFC6890" list=bad_ipv4
add address=192.0.0.0/24 comment="defconf: RFC6890" list=bad_ipv4
add address=192.0.2.0/24 comment="defconf: RFC6890 documentation" list=bad_ipv4
add address=198.51.100.0/24 comment="defconf: RFC6890 documentation" list=bad_ipv4
add address=203.0.113.0/24 comment="defconf: RFC6890 documentation" list=bad_ipv4
add address=240.0.0.0/4 comment="defconf: RFC6890 reserved" list=bad_ipv4

Non-Globally-Routable IPs:

/ip firewall address-list
add address=0.0.0.0/8 comment="defconf: RFC6890" list=not_global_ipv4
add address=10.0.0.0/8 comment="defconf: RFC6890" list=not_global_ipv4
add address=100.64.0.0/10 comment="defconf: RFC6890" list=not_global_ipv4
add address=169.254.0.0/16 comment="defconf: RFC6890" list=not_global_ipv4
add address=172.16.0.0/12 comment="defconf: RFC6890" list=not_global_ipv4
add address=192.0.0.0/29 comment="defconf: RFC6890" list=not_global_ipv4
add address=192.168.0.0/16 comment="defconf: RFC6890" list=not_global_ipv4
add address=198.18.0.0/15 comment="defconf: RFC6890 benchmark" list=not_global_ipv4
add address=255.255.255.255/32 comment="defconf: RFC6890" list=not_global_ipv4

Invalid Source and Destination Addresses:

/ip firewall address-list
add address=224.0.0.0/4 comment="defconf: multicast" list=bad_src_ipv4
add address=255.255.255.255/32 comment="defconf: RFC6890" list=bad_src_ipv4
add address=0.0.0.0/8 comment="defconf: RFC6890" list=bad_dst_ipv4
add address=224.0.0.0/4 comment="defconf: RFC6890" list=bad_dst_ipv4
/ip firewall raw
add action=accept chain=prerouting comment="defconf: enable for transparent firewall" disabled=yes
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
add action=drop chain=prerouting comment="defconf: drop bogon IP's" src-address-list=bad_ipv4
add action=drop chain=prerouting comment="defconf: drop bogon IP's" dst-address-list=bad_ipv4
add action=drop chain=prerouting comment="defconf: drop bogon IP's" src-address-list=bad_src_ipv4
add action=drop chain=prerouting comment="defconf: drop bogon IP's" dst-address-list=bad_dst_ipv4
add action=drop chain=prerouting comment="defconf: drop non global from WAN" src-address-list=not_global_ipv4 in-interface-list=WAN
add action=drop chain=prerouting comment="defconf: drop forward to local lan from WAN" in-interface-list=WAN dst-address=192.168.88.0/24
add action=drop chain=prerouting comment="defconf: drop local if not from default IP range" in-interface-list=LAN src-address=!192.168.88.0/24
add action=drop chain=prerouting comment="defconf: drop bad UDP" port=0 protocol=udp
add action=jump chain=prerouting comment="defconf: jump to ICMP chain" jump-target=icmp4 protocol=icmp
add action=jump chain=prerouting comment="defconf: jump to TCP chain" jump-target=bad_tcp protocol=tcp
add action=accept chain=prerouting comment="defconf: accept everything else from LAN" in-interface-list=LAN
add action=accept chain=prerouting comment="defconf: accept everything else from WAN" in-interface-list=WAN
add action=accept chain=prerouting comment="defconf: accept local traffic between router interfaces" src-address-type=local
add action=drop chain=prerouting comment="defconf: drop the rest"

TCP Flag Validation Chain:

/ip firewall raw
add action=drop chain=bad_tcp comment="defconf: TCP flag filter" protocol=tcp tcp-flags=!fin,!syn,!rst,!ack
add action=drop chain=bad_tcp comment=defconf protocol=tcp tcp-flags=fin,syn
add action=drop chain=bad_tcp comment=defconf protocol=tcp tcp-flags=fin,rst
add action=drop chain=bad_tcp comment=defconf protocol=tcp tcp-flags=fin,!ack
add action=drop chain=bad_tcp comment=defconf protocol=tcp tcp-flags=fin,urg
add action=drop chain=bad_tcp comment=defconf protocol=tcp tcp-flags=syn,rst
add action=drop chain=bad_tcp comment=defconf protocol=tcp tcp-flags=rst,urg
add action=drop chain=bad_tcp comment="defconf: TCP port 0 drop" port=0 protocol=tcp

ICMP Validation Chain:

/ip firewall raw
add action=accept chain=icmp4 comment="defconf: echo reply" icmp-options=0:0 limit=5,10:packet protocol=icmp
add action=accept chain=icmp4 comment="defconf: net unreachable" icmp-options=3:0 protocol=icmp
add action=accept chain=icmp4 comment="defconf: host unreachable" icmp-options=3:1 protocol=icmp
add action=accept chain=icmp4 comment="defconf: protocol unreachable" icmp-options=3:2 protocol=icmp
add action=accept chain=icmp4 comment="defconf: port unreachable" icmp-options=3:3 protocol=icmp
add action=accept chain=icmp4 comment="defconf: fragmentation needed" icmp-options=3:4 protocol=icmp
add action=accept chain=icmp4 comment="defconf: echo" icmp-options=8:0 limit=5,10:packet protocol=icmp
add action=accept chain=icmp4 comment="defconf: time exceeded " icmp-options=11:0-255 protocol=icmp
add action=drop chain=icmp4 comment="defconf: drop other icmp" protocol=icmp

The ICMP rate limiting (limit=5,10:packet) restricts ping responses to 5 packets per 10 seconds, preventing ICMP-based DoS attacks while maintaining diagnostic functionality. This strict ICMP filtering is optional and typically unnecessary in most environments since the Linux kernel already limits ICMP to 100 packets per second.

Bogon and Invalid IPv6 Addresses:

/ipv6 firewall address-list
add address=::1/128 comment="defconf: RFC6890 lo" list=bad_ipv6
add address=::ffff:0:0/96 comment="defconf: RFC6890 IPv4 mapped" list=bad_ipv6
add address=2001::/23 comment="defconf: RFC6890" list=bad_ipv6
add address=2001:db8::/32 comment="defconf: RFC6890 documentation" list=bad_ipv6
add address=2001:10::/28 comment="defconf: RFC6890 orchid" list=bad_ipv6
add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6

Non-Globally-Routable IPv6 Addresses:

/ipv6 firewall address-list
add address=100::/64 comment="defconf: RFC6890 Discard-only" list=not_global_ipv6
add address=2001::/32 comment="defconf: RFC6890 TEREDO" list=not_global_ipv6
add address=2001:2::/48 comment="defconf: RFC6890 Benchmark" list=not_global_ipv6
add address=fc00::/7 comment="defconf: RFC6890 Unique-Local" list=not_global_ipv6

Invalid Source and Destination:

/ipv6 firewall address-list
add address=::/128 comment="defconf: unspecified" list=bad_dst_ipv6
add address=::/128 comment="defconf: unspecified" list=bad_src_ipv6
add address=ff00::/8 comment="defconf: multicast" list=bad_src_ipv6
/ipv6 firewall raw
add action=accept chain=prerouting comment="defconf: enable for transparent firewall" disabled=yes
add action=accept chain=prerouting comment="defconf: RFC4291, section 2.7.1" src-address=::/128 dst-address=ff02:0:0:0:0:1:ff00::/104 icmp-options=135 protocol=icmpv6
add action=drop chain=prerouting comment="defconf: drop bogon IP's" src-address-list=bad_ipv6
add action=drop chain=prerouting comment="defconf: drop bogon IP's" dst-address-list=bad_ipv6
add action=drop chain=prerouting comment="defconf: drop packets with bad SRC ipv6" src-address-list=bad_src_ipv6
add action=drop chain=prerouting comment="defconf: drop packets with bad dst ipv6" dst-address-list=bad_dst_ipv6
add action=drop chain=prerouting comment="defconf: drop non global from WAN" src-address-list=not_global_ipv6 in-interface-list=WAN
add action=jump chain=prerouting comment="defconf: jump to ICMPv6 chain" jump-target=icmp6 protocol=icmpv6
add action=accept chain=prerouting comment="defconf: accept local multicast scope" dst-address=ff02::/16
add action=drop chain=prerouting comment="defconf: drop other multicast destinations" dst-address=ff00::/8
add action=accept chain=prerouting comment="defconf: accept everything else from WAN" in-interface-list=WAN
add action=accept chain=prerouting comment="defconf: accept everything else from LAN" in-interface-list=LAN
add action=drop chain=prerouting comment="defconf: drop the rest"

ICMPv6 Validation Chain:

/ipv6 firewall raw
add action=drop chain=icmp6 comment="defconf: rfc4890 drop ll if hop-limit!=255" dst-address=fe80::/10 hop-limit!=255 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: dst unreachable" icmp-options=1:0-255 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: packet too big" icmp-options=2:0-255 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: limit exceeded" icmp-options=3:0-1 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: bad header" icmp-options=4:0-2 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: Mobile home agent address discovery" icmp-options=144:0-255 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: Mobile home agent address discovery" icmp-options=145:0-255 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: Mobile prefix solic" icmp-options=146:0-255 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: Mobile prefix advert" icmp-options=147:0-255 protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: echo request limit 5,10" icmp-options=128:0-255 limit=5,10:packet protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: echo reply limit 5,10" icmp-options=129:0-255 limit=5,10:packet protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: rfc4890 router solic limit 5,10 only LAN" hop-limit=equal:255 icmp-options=133:0-255 in-interface-list=LAN limit=5,10:packet protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: rfc4890 router advert limit 5,10 only LAN" hop-limit=equal:255 icmp-options=134:0-255 in-interface-list=LAN limit=5,10:packet protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: rfc4890 neighbor solic limit 5,10 only LAN" hop-limit=equal:255 icmp-options=135:0-255 in-interface-list=LAN limit=5,10:packet protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: rfc4890 neighbor advert limit 5,10 only LAN" hop-limit=equal:255 icmp-options=136:0-255 in-interface-list=LAN limit=5,10:packet protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: rfc4890 inverse ND solic limit 5,10 only LAN" hop-limit=equal:255 icmp-options=141:0-255 in-interface-list=LAN limit=5,10:packet protocol=icmpv6
add action=accept chain=icmp6 comment="defconf: rfc4890 inverse ND advert limit 5,10 only LAN" hop-limit=equal:255 icmp-options=142:0-255 in-interface-list=LAN limit=5,10:packet protocol=icmpv6
add action=drop chain=icmp6 comment="defconf: drop other icmp" protocol=icmpv6

The hop-limit validation ensures that ICMPv6 packets received from off-link sources are dropped, preventing certain types of hop-limit-based attacks. Router and neighbor discovery messages are only accepted from the local link (LAN interface-list) with proper hop-limit of 255.

Connection Rate-Based Traffic Prioritization

Section titled “Connection Rate-Based Traffic Prioritization”

The connection-rate matcher enables traffic classification based on connection speed, allowing identification and prioritization of different traffic types for QoS implementation.

Connection Rate calculates the current speed of a connection based on the change in connection-bytes value. The connection rate is recalculated every second without averaging, providing real-time speed measurements. Both connection-bytes and connection-rate work with TCP and UDP traffic when a protocol is specified.

The syntax for connection-rate allows specifying a range: connection-rate=From-To where values range from 0 to 4294967295 bytes per second. Use suffixes like k for kilobytes and M for megabytes for convenience.

Example - Capturing slow connections:

/ip firewall filter
add action=accept chain=forward connection-rate=0-100k protocol=tcp
add action=accept chain=forward connection-rate=0-100k protocol=udp

Connection rate detection enables identifying “heavy” connections (P2P, large downloads, streaming) versus “light” connections (VoIP, gaming, web browsing) based on sustained throughput. This allows prioritizing interactive traffic while limiting bulk transfers.

For this example, assume HTTP browsing transfers less than 500KB total and VoIP requires less than 200kbps. Connections exceeding 500KB with sustained speed above 200kbps are classified as heavy traffic.

Mangle Rules for Connection Classification:

/ip firewall mangle
add chain=forward action=mark-connection connection-mark=!heavy_traffic_conn new-connection-mark=all_conn
add chain=forward action=mark-connection connection-bytes=500000-0 connection-mark=all_conn connection-rate=200k-100M new-connection-mark=heavy_traffic_conn protocol=tcp
add chain=forward action=mark-connection connection-bytes=500000-0 connection-mark=all_conn connection-rate=200k-100M new-connection-mark=heavy_traffic_conn protocol=udp
add chain=forward action=mark-packet connection-mark=heavy_traffic_conn new-packet-mark=heavy_traffic passthrough=no
add chain=forward action=mark-packet connection-mark=all_conn new-packet-mark=other_traffic passthrough=no

The first rule marks all connections not already classified as heavy. Subsequent rules identify heavy connections based on total bytes transferred (over 500KB) and sustained rate (over 200kbps). Finally, packets are marked according to their connection classification.

Queue Tree Configuration:

/queue tree
add name=upload parent=public max-limit=6M
add name=other_upload parent=upload limit-at=4M max-limit=6M packet-mark=other_traffic priority=1
add name=heavy_upload parent=upload limit-at=2M max-limit=6M packet-mark=heavy_traffic priority=8
add name=download parent=local max-limit=6M
add name=other_download parent=download limit-at=4M max-limit=6M packet-mark=other_traffic priority=1
add name=heavy_download parent=download limit-at=2M max-limit=6M packet-mark=heavy_traffic priority=8

Priority 1 (highest) is assigned to other_traffic including VoIP and web browsing, while priority 8 (lowest) is assigned to heavy_traffic. The limit-at guarantees minimum bandwidth while max-limit caps maximum throughput.

The complete configuration for a 6Mbps connection prioritizing interactive traffic:

/ip firewall mangle
add chain=forward action=mark-connection connection-mark=!heavy_traffic_conn new-connection-mark=all_conn
add chain=forward action=mark-connection connection-bytes=500000-0 connection-mark=all_conn connection-rate=200k-100M new-connection-mark=heavy_traffic_conn protocol=tcp
add chain=forward action=mark-connection connection-bytes=500000-0 connection-mark=all_conn connection-rate=200k-100M new-connection-mark=heavy_traffic_conn protocol=udp
add chain=forward action=mark-packet connection-mark=heavy_traffic_conn new-packet-mark=heavy_traffic passthrough=no
add chain=forward action=mark-packet connection-mark=all_conn new-packet-mark=other_traffic passthrough=no
/queue tree
add name=upload parent=public max-limit=6M
add name=other_upload parent=upload limit-at=4M max-limit=6M packet-mark=other_traffic priority=1
add name=heavy_upload parent=upload limit-at=2M max-limit=6M packet-mark=heavy_traffic priority=8
add name=download parent=local max-limit=6M
add name=other_download parent=download limit-at=4M max-limit=6M packet-mark=other_traffic priority=1
add name=heavy_download parent=download limit-at=2M max-limit=6M packet-mark=heavy_traffic priority=8

Adjust the connection-bytes threshold (500KB) and connection-rate threshold (200kbps) based on actual network usage patterns. HTTP browsing may transfer more than 500KB on modern websites, and VoIP quality may require different bandwidth guarantees.

  • RFC 6890: Special-Purpose Address Registries
  • RFC 4291: IP Version 6 Addressing Architecture
  • RFC 4890: Recommendations for Filtering ICMPv6 Messages
  • RAW Firewall: High-performance packet filtering before connection tracking
  • Connection Tracking: Stateful inspection and traffic classification
  • Queue Trees: Hierarchical token bucket for QoS implementation