Skip to content

Firewall Address Lists

For the impatient: create a whitelist and blacklist.

# Create trusted whitelist (permanent)
/ip firewall address-list add list=trusted address=192.168.1.0/24 comment="LAN"
/ip firewall address-list add list=trusted address=10.0.0.0/8 comment="VPN"
# Create temporary blacklist entry (expires in 1 day)
/ip firewall address-list add list=blacklist address=203.0.113.50 timeout=1d
# Use in firewall rules
/ip firewall filter add chain=input action=accept src-address-list=trusted
/ip firewall filter add chain=input action=drop src-address-list=blacklist

Verify:

/ip firewall address-list print where list=trusted
/ip firewall address-list print where list=blacklist

What this does: Address lists group IP addresses, ranges, CIDR blocks, and DNS names under a common name. Firewall rules can then match against the entire list instead of individual addresses, simplifying rule management and enabling dynamic behaviors like brute force protection.

When to use this:

  • Whitelisting trusted networks or management IPs
  • Blacklisting attackers, spammers, or threat feeds
  • Implementing port knocking or brute force protection
  • Geographic IP blocking
  • Grouping servers for NAT or routing policies

Key Concepts:

Entry TypeStorageSurvives RebootUse Case
StaticDisk (NAND)YesPermanent whitelists, RFC bogons
DynamicRAMNoTemporary blocks, threat feeds

Prerequisites:

  • Basic firewall filter rules configured
  • Connection tracking enabled (for connection-state matching)
  • DNS configured (for FQDN entries)

Static entries are permanent and survive reboots. Use for trusted networks.

/ip firewall address-list add list=trusted address=192.168.1.0/24 comment="LAN"
/ip firewall address-list add list=trusted address=10.0.0.0/8 comment="VPN networks"

Supported address formats:

FormatExampleNotes
Single IP192.168.1.1Single host
CIDR192.168.1.0/24Network prefix
Range192.168.0.0-192.168.1.255Auto-converts to CIDR
DNS nameoffice.example.comResolved automatically

Dynamic entries have a timeout and are stored in RAM. Use for temporary blocks.

# Block for 1 hour
/ip firewall address-list add list=temp-block address=203.0.113.50 timeout=1h
# Block for 1 day
/ip firewall address-list add list=blacklist address=198.51.100.25 timeout=1d

Timeout formats:

  • 30s - 30 seconds
  • 10m - 10 minutes
  • 2h - 2 hours
  • 1d - 1 day
  • 2w - 2 weeks
  • 1d2h30m - Combined format

Address lists can resolve DNS names automatically:

/ip firewall address-list add list=allowed address=myoffice.dyndns.org

Behavior:

  • RouterOS resolves the FQDN and adds IPs as dynamic entries
  • Resolution follows DNS TTL for refresh timing
  • The FQDN entry survives reboot; resolved IPs are dynamic

To force re-resolution:

/ip firewall address-list set [find where address~"dyndns.org"] disabled=yes
/ip firewall address-list set [find where address~"dyndns.org"] disabled=no

Match traffic against address lists:

# Accept from trusted sources
/ip firewall filter add chain=input action=accept src-address-list=trusted
# Drop blacklisted sources
/ip firewall filter add chain=input action=drop src-address-list=blacklist
# NAT only for non-internal destinations
/ip firewall nat add chain=srcnat action=masquerade out-interface=ether1 \
dst-address-list=!internal-networks

Matchers:

  • src-address-list - Match packet source IP
  • dst-address-list - Match packet destination IP

Common Mistakes

  • Only ONE address list can be specified per matcher - to match multiple lists, use separate rules or combine the lists
  • Always add your trusted IPs to a whitelist BEFORE enabling blacklist rules to avoid self-lockout

Firewall rules can automatically add IPs to address lists based on traffic patterns.

Adds packet’s source IP to a list:

/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=ssh-attempts address-list-timeout=1h \
protocol=tcp dst-port=22 connection-state=new

Adds packet’s destination IP to a list:

/ip firewall mangle add chain=prerouting action=add-dst-to-address-list \
address-list=visited-sites address-list-timeout=1h \
protocol=tcp dst-port=80,443

Important: These actions operate in passthrough mode - packets continue to subsequent rules.

Staged blocking with escalating timeouts:

# Drop blacklisted first (place at top of input chain)
/ip firewall filter add chain=input action=drop \
protocol=tcp dst-port=22 src-address-list=ssh_blacklist
# Stage 1: First connection -> stage1 list (1 min)
/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=ssh_stage1 address-list-timeout=1m \
protocol=tcp dst-port=22 connection-state=new
# Stage 2: If in stage1 -> stage2 (1 min)
/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=ssh_stage2 address-list-timeout=1m \
protocol=tcp dst-port=22 connection-state=new \
src-address-list=ssh_stage1
# Stage 3: If in stage2 -> stage3 (1 min)
/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=ssh_stage3 address-list-timeout=1m \
protocol=tcp dst-port=22 connection-state=new \
src-address-list=ssh_stage2
# Stage 4: If in stage3 -> blacklist (10 days)
/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=ssh_blacklist address-list-timeout=10d \
protocol=tcp dst-port=22 connection-state=new \
src-address-list=ssh_stage3

How it works: Each new connection advances the attacker through stages. Legitimate users who wait 1 minute between connections start fresh. After 4 rapid connections, the IP is blacklisted for 10 days.

Sequential port access grants firewall bypass:

# Knock 1: Port 8888 -> added to knock1 for 30s
/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=knock1 address-list-timeout=30s \
protocol=tcp dst-port=8888 in-interface-list=WAN
# Knock 2: Port 7777 (only if in knock1) -> knock2 for 30s
/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=knock2 address-list-timeout=30s \
protocol=tcp dst-port=7777 in-interface-list=WAN \
src-address-list=knock1
# Knock 3: Port 6666 (only if in knock2) -> secured for 30m
/ip firewall filter add chain=input action=add-src-to-address-list \
address-list=secured address-list-timeout=30m \
protocol=tcp dst-port=6666 in-interface-list=WAN \
src-address-list=knock2
# Accept traffic from secured list
/ip firewall filter add chain=input action=accept \
in-interface-list=WAN src-address-list=secured

Client access (Linux):

Terminal window
for port in 8888 7777 6666; do nmap -Pn -p $port router.example.com; done

Group interfaces for use in firewall rules:

# Create lists
/interface list add name=WAN comment="Internet-facing"
/interface list add name=LAN comment="Internal networks"
# Add members
/interface list member add interface=ether1 list=WAN
/interface list member add interface=bridge list=LAN
# Use in firewall
/ip firewall filter add chain=input action=accept in-interface-list=LAN
/ip firewall filter add chain=input action=drop in-interface-list=WAN

Matchers:

  • in-interface-list - Match incoming interface
  • out-interface-list - Match outgoing interface

Block invalid source addresses:

/ip firewall address-list
add list=bogons address=0.0.0.0/8 comment="RFC6890: This host"
add list=bogons address=127.0.0.0/8 comment="RFC6890: Loopback"
add list=bogons address=192.0.0.0/24 comment="RFC6890: IETF Protocol"
add list=bogons address=192.0.2.0/24 comment="RFC5737: TEST-NET-1"
add list=bogons address=198.51.100.0/24 comment="RFC5737: TEST-NET-2"
add list=bogons address=203.0.113.0/24 comment="RFC5737: TEST-NET-3"
add list=bogons address=224.0.0.0/4 comment="RFC5771: Multicast"
add list=bogons address=240.0.0.0/4 comment="RFC1112: Reserved"
# Private addresses (block from WAN)
add list=private address=10.0.0.0/8 comment="RFC1918"
add list=private address=172.16.0.0/12 comment="RFC1918"
add list=private address=192.168.0.0/16 comment="RFC1918"
add list=private address=169.254.0.0/16 comment="RFC3927: Link-local"
add list=private address=100.64.0.0/10 comment="RFC6598: CGN"
# Drop bogons from WAN
/ip firewall filter add chain=input action=drop in-interface-list=WAN \
src-address-list=bogons comment="Drop bogon sources"
/ip firewall filter add chain=input action=drop in-interface-list=WAN \
src-address-list=private comment="Drop private sources from WAN"

Download and import external blocklists:

# Fetch DShield block list
/tool fetch url="https://feeds.dshield.org/block.txt" dst-path=dshield.txt
# Import (requires .rsc format)
/import file-name=dshield.rsc

Limitations:

  • Maximum file size: 63 KiB (larger files get truncated)
  • Import halts on duplicate entries (pre-filter your lists)
# View all address lists
/ip firewall address-list print
# View specific list
/ip firewall address-list print where list=blacklist
# View dynamic entries only
/ip firewall address-list print where dynamic=yes
# View with timeout countdown
/ip firewall address-list print detail
# Count entries per list
/ip firewall address-list print count-only where list=blacklist
# Check firewall rule hit counters
/ip firewall filter print stats where src-address-list~"."
# View interface lists
/interface list print
/interface list member print

Expected result: Address list entries visible with appropriate flags (D for dynamic). Firewall rules show hit counters increasing when traffic matches.

ScenarioUse RAWUse Filter
DDoS mitigation (high volume)YesNo
Large blacklist (10k+ entries)YesNo
Small list, low match rateNoYes
Need connection stateNoYes
Need TCP reset on rejectNoYes
# RAW-based blacklist (most efficient for DDoS)
/ip firewall raw add chain=prerouting src-address-list=blacklist action=drop
  1. Accept established/related first (most frequent)
  2. Drop invalid connections
  3. Accept specific allowed traffic
  4. Blacklist checks
  5. Default drop
SymptomCauseSolution
Self-lockout from SSHBrute force rules triggeredAdd trusted IPs to whitelist first
DNS entries not updatingTTL not expiredDisable/enable entry to force refresh
Dynamic entries gone after rebootTimeout entries are RAM-onlyUse static entries or recreate on boot
Rule not matching listTypo in list nameVerify exact list name match
Import stops partwayDuplicate entries in filePre-filter duplicates before import
Script removes all entriesUnfiltered find commandStore entry ID before removal
Large list slowing router27k+ entriesUse RAW, aggregate ranges, optimize order
Timeout won’t decreaseBy designUse separate lists if needed

Common Mistakes

  • Don’t omit timeout for large/frequently-updated lists - causes NAND wear with constant writes
  • Don’t assume timeout decreases work - they’re ignored by design to prevent rule conflicts
  • Don’t initialize lists with network addresses (192.168.1.0/24) - may cause mangle rule issues
  • Don’t forget that “bots can come from any country” - geographic filtering is supplementary only
:local ip "1.2.3.4"
:local listname "blacklist"
:if ([:len [/ip firewall address-list find address=$ip list=$listname]] = 0) do={
/ip firewall address-list add list=$listname address=$ip timeout=1d
}
:local ip "1.2.3.4"
:local listname "blacklist"
:local entryID [/ip firewall address-list find address=$ip list=$listname]
:if ([:len $entryID] > 0) do={
/ip firewall address-list remove $entryID
}
/ip firewall address-list remove [find where list=blacklist dynamic=yes]