Skip to content

Lab 06-08 · Connection tracking: a stateful firewall

MTCNA

Module 6 · Firewall of the MikroTik MTCNA course, official slides p274–282 (connection states, connection tracking, FastTrack and the packet flow), applied to the firewall of slides p241–251.

At a festival you show your ticket once. The staff stamp your hand, and from then on you walk out for a sandwich and back in without queuing again: the stamp proves you already belong. Nobody checks your ticket a second time, and nobody without a stamp sneaks in pretending to "come back".

Your router can stamp hands too. It remembers every conversation that started from inside (that memory is connection tracking), so it can apply one very powerful rule: "from the Internet, let in only answers to conversations we started; drop everything new". That rule is the heart of every home router's firewall, including MikroTik's default configuration. In this lab you build it, and on the way you fall into the trap that catches almost every beginner.

What you will learn

  • Name the four connection states: new, established, related and invalid.
  • Explain connection tracking and why NAT and stateful filtering depend on it.
  • Build a stateful firewall for input and forward that blocks new connections from the WAN.
  • Fall into, and understand, the trap of dropping the WAN without accepting established traffic first.
  • Explain what FastTrack does and why it makes a router faster.

Connection states: the hand stamp

Connection tracking is the router's memory of every active conversation (connection) that crosses it or reaches it.

Every packet the router sees is compared with that memory and gets a connection state (slide 274):

State Meaning At the festival
new the packet opens a new connection a face never seen: show your ticket
established the packet belongs to a connection already known stamped hand: walk right in
related a new connection, but linked to a known one (e.g. an ICMP error about it, FTP data) your stamped friend's plus-one, announced in advance
invalid the packet does not belong to any known connection and does not make sense a stamp drawn with a felt-tip pen

RouterOS also knows a fifth state, untracked, for traffic you deliberately exclude from connection tracking with raw rules. The MTCNA course does not use it, so this lab sticks to the four above.

Connection state is not TCP state

TCP has its own states (syn-sent, established, time-wait...). The firewall's states are different and also apply to UDP and ICMP, which have no states of their own. A DNS question (udp) followed by its answer is a connection too: the question is new, the answer is established.

Slide 276: connection tracking must be enabled for NAT and for state-based filtering to work. In IP → Firewall → Connections you can see every tracked connection; the Tracking button shows the settings (enabled=auto means "on as soon as there is any firewall rule").

The stateful recipe

A stateful firewall uses the states instead of listing every allowed service. For each chain:

  1. accept established, related: stamped hands walk in. First, because it matches most packets.
  2. drop invalid: fake stamps out.
  3. accept the few new things you want (here: ping to the router).
  4. drop what is new and comes from the WAN.
flowchart TB
    P["packet arrives"] --> E{"established<br>or related?"}
    E -- "yes" --> A["<b>accept</b><br>(stamped hand)"]
    E -- "no" --> I{"invalid?"}
    I -- "yes" --> D1["<b>drop</b>"]
    I -- "no: it is new" --> W{"from the WAN<br>(ether2)?"}
    W -- "yes" --> D2["<b>drop</b><br>(unless port-forwarded)"]
    W -- "no: from the LAN" --> A2["<b>accept</b>"]

The forward chain's last rule adds connection-nat-state=!dstnat: "drop new connections from the WAN unless a dst-nat rule (lab 06-05) deliberately sent them in". That is how port forwarding and a strict firewall live together.

FastTrack: the stamped-hands fast lane

Slide 278: FastTrack lets packets of established and related connections skip most of the router's work (firewall, connection tracking updates, simple queues...). It currently supports TCP and UDP. The rule is action=fasttrack-connection in forward; it is followed by a normal accept rule for the packets that cannot be fast-tracked. Slide 279's test on an RB2011 with one TCP stream: 360 Mbps without FastTrack, 890 Mbps with it, and the firewall's share of CPU fell from 44% to 6%.

The price of the fast lane

Fast-tracked packets skip the firewall and queues. If you later add a bandwidth limit (Module 7, queues) and it "does not work", FastTrack is the usual suspect.

Slides 280–282 show the full packet flow: where input, forward, output, dstnat and srcnat sit on a packet's way through RouterOS. Notice that dstnat happens before the routing decision (so the router knows where the new destination is) and srcnat happens last, just before the packet leaves.

The scenario

On a real hAP the Internet port is ether1; in the virtual lab ether1 is reserved for the lab itself, so the Internet port is ether2 and the LAN ports start at ether3.

flowchart LR
    PC1["<b>pc1</b> (your laptop)<br>192.168.88.200"] -- "LAN" --- R1["<b>r1</b> (the club)<br>bridge1: 192.168.88.1/24<br>ether2 = WAN: 10.0.0.x"]
    R1 -- "class network 10.0.0.0/24<br>(the neighbourhood)" --- AP["<b>class-ap</b><br>10.0.0.1 · 198.51.100.1<br>plays the nosy neighbour"]
    AP --- NET["<b>internet</b><br>198.51.100.10<br>plays the stranger"]
Node Role Address
pc1 your laptop 192.168.88.200 (DHCP)
r1 your router, with an empty filter LAN 192.168.88.1/24 · WAN ether2 10.0.0.x
class-ap trainer's router; here it also plays a neighbour on your WAN network 10.0.0.1
internet a stranger on the Internet 198.51.100.10

Start the lab

Download the lab files and unzip them: 06-08-connection-tracking.zip. Open a terminal inside the 06-08-connection-tracking folder.

From the lab folder guions/tema-3/lab/mtcna/06-08-connection-tracking/:

sudo containerlab deploy -t topology.clab.yml

After about two minutes:

ssh admin@clab-mtcna-06-08-r1                       # your router (password: admin)
ssh admin@clab-mtcna-06-08-class-ap                 # the neighbour (password: admin)
docker exec -it clab-mtcna-06-08-pc1 bash           # your laptop
docker exec -it clab-mtcna-06-08-internet bash      # the stranger

Find r1's WAN address with /ip address print where interface=ether2 (below: WAN, e.g. 10.0.0.200).

Step 1 · See how exposed the router is

On internet (use your WAN address):

curl -s http://10.0.0.200 | grep -o '<title>.*</title>'     # <title>RouterOS</title>

A stranger on the Internet reaches your router's login page. Now the neighbour: on class-ap, add a route to your LAN through your router and ping your laptop:

/ip route add dst-address=192.168.88.0/24 gateway=10.0.0.200 comment=attacker
/ping 192.168.88.200 count=2

It answers. NAT alone is not a firewall: anybody next to your WAN port who knows the trick can walk straight into your LAN.

Check yourself

r1 already masquerades the LAN. Why did that not stop the neighbour from pinging your laptop?

Done when: both the stranger and the neighbour got in.

Step 2 · The beginner's trap: drop the WAN, and nothing else

The obvious idea: "drop everything that comes into the router from the WAN".

IP → Firewall → Filter Rules → + · Chain input, In. Interface ether2 · Action drop · Comment input: drop everything else from the Internet (WAN)

/ip firewall filter add chain=input in-interface=ether2 action=drop comment="input: drop everything else from the Internet (WAN)"
/ip dns cache flush

(dns cache flush makes the router forget the names it already knew, so it must ask again.) On pc1:

curl -s --max-time 6 http://www.mikrotik.com; echo "exit $?"     # exit 28: broken!

Your laptop asks the router for DNS, the router asks class-ap... and class-ap's answer arrives on ether2, addressed to the router: input chain, from the WAN, dropped. You locked out your own answers.

Check yourself

Explain the failure with the festival analogy: who is the guest coming back, and why was he refused?

Done when: you saw the web break and can explain why.

Add three rules above the drop: accept established/related, drop invalid, accept ping.

IP → Firewall → Filter Rules → + · Chain input, Connection State established, related · Action accept; then Connection State invalid · drop; then Protocol 1 (icmp) · accept. Drag the three above the WAN drop.

/ip firewall filter add chain=input action=accept connection-state=established,related comment="input: welcome back (established, related)" place-before=[find comment="input: drop everything else from the Internet (WAN)"]
/ip firewall filter add chain=input action=drop connection-state=invalid comment="input: drop invalid" place-before=[find comment="input: drop everything else from the Internet (WAN)"]
/ip firewall filter add chain=input action=accept protocol=icmp comment="input: accept ping" place-before=[find comment="input: drop everything else from the Internet (WAN)"]
/ip firewall filter print

Test:

# on pc1
curl -s http://www.mikrotik.com                                  # Welcome to the Internet
# on internet
curl -s --max-time 4 http://10.0.0.200; echo "exit $?"           # exit 28: WebFig closed
ping -c2 10.0.0.200                                              # answers: ping accepted on purpose

And the neighbour, on class-ap:

/ping 192.168.88.200 count=2

Still answers! Input protects the router; the neighbour's ping goes through it to your laptop: forward.

Check yourself

The stranger's ping to the WAN is answered on purpose. Give one reason to allow it and one reason some admins block it.

Done when: pc1 browses, the stranger cannot open WebFig, ping to the WAN works.

Step 4 · Protect the corridor too

The same recipe in forward, plus FastTrack. Add them in this order (new rules go to the bottom, which is what we want here):

/ip firewall filter add chain=forward action=fasttrack-connection connection-state=established,related comment="forward: fasttrack known connections"
/ip firewall filter add chain=forward action=accept connection-state=established,related comment="forward: welcome back (established, related)"
/ip firewall filter add chain=forward action=drop connection-state=invalid comment="forward: drop invalid"
/ip firewall filter add chain=forward action=drop in-interface=ether2 connection-state=new connection-nat-state=!dstnat comment="forward: drop new from the WAN unless port-forwarded"

In WinBox, the last one: Chain forward, In. Interface ether2, Connection State new, and Connection NAT State !dstnat (tick the ! box: "not") · Action drop.

On class-ap:

/ping 192.168.88.200 count=2          # timeout: the neighbour is out
/ip route remove [find comment=attacker]

On pc1 everything still works:

curl -s http://www.mikrotik.com       # Welcome to the Internet

Check yourself

Why does the last forward rule say connection-nat-state=!dstnat? What would happen to the port forward of lab 06-05 without it?

Done when: the neighbour's ping times out and pc1 still browses.

Step 5 · Read the hand stamps

IP → Firewall → Filter Rules (counters) and IP → Firewall → Connections

/ip firewall filter print stats
/ip firewall connection print where src-address~"192.168.88.200"

Notice:

  • the established,related accept rules have by far the biggest counters: most packets are answers;
  • the WAN drop rules counted the stranger's and the neighbour's attempts;
  • the router added a dynamic rule special dummy rule to show fasttrack counters: it appears as soon as a fasttrack rule exists, to display how many packets took the fast lane.

Run the checker from the lab folder on your PC:

CLAB_EXEC="sudo docker exec" ./check.sh

It plays the stranger and the neighbour for you and must end with RESULT: lab 06-08 solved.

Check yourself

Compare the counter of the input accept established,related rule with the WAN drop rule. What does the difference tell you about normal traffic?

Done when: check.sh passes.

If something goes wrong

Symptom Likely cause Fix
pc1 cannot browse after adding the rules accept established,related is below the WAN drop move it to the top of its chain
Only names fail; curl http://198.51.100.10 works input drops the DNS answers to the router: same cause, input chain move the input established rule up
The stranger can still open WebFig the WAN drop uses in-interface=bridge1, or is disabled in-interface=ether2
Ping to the WAN does not answer the icmp accept is below the WAN drop move it up
The neighbour still reaches pc1 the forward drop is missing in-interface=ether2 / connection-state=new, or is disabled check it with print detail
A port forward (lab 06-05) stopped working the forward drop lacks connection-nat-state=!dstnat add it

Clean up

sudo containerlab destroy -t topology.clab.yml --cleanup

Key takeaways

  • Connection tracking remembers every connection; each packet gets a state: new, established, related or invalid.
  • A stateful firewall accepts established, related first, drops invalid, and drops new connections from the WAN.
  • Dropping the WAN without accepting established traffic first breaks your own DNS and browsing.
  • In forward, connection-nat-state=!dstnat keeps port forwarding working behind a strict firewall.
  • FastTrack lets established/related TCP and UDP packets skip most processing: much faster, but they also skip firewall and queues.
  • NAT is not a firewall: without forward rules, a neighbour with a route can reach your LAN.

Quick check

1. A packet is the first one of a brand-new conversation. Which connection state does it have?
  • new
  • established
  • related
  • invalid

The first packet of a connection the router has never seen is new. Its replies, and everything after, are established.

2. What does the related state mean?
  • A packet that belongs to an existing connection
  • A new connection that is linked to an existing one
  • A packet that does not belong to any connection
  • A packet coming from a related subnet

related marks a new connection the router expected because of an existing one, such as an ICMP error about it or an FTP data connection.

3. You add input in-interface=ether2 action=drop as the only rule. Why does web browsing on the LAN break?
  • The rule also drops traffic from bridge1
  • The answers to the router's own DNS questions arrive on ether2 and are dropped
  • It disables NAT
  • It drops ping

The LAN asks the router for DNS; the router asks upstream; the answer comes back through ether2 to the router (input) and is dropped. Accept established, related above the drop.

4. Why is accept established,related placed first in each chain?
  • Because RouterOS requires it at position 0
  • Because it only works in the first position
  • Because most packets are answers, so they are matched early and nothing below can drop them
  • Because FastTrack must come last

Answers to your own connections are the bulk of traffic. Matching them first keeps them safe from later drops and saves the router from reading the rest of the list.

5. What does FastTrack do?
  • It blocks connections faster
  • It lets established and related TCP/UDP packets skip most router processing
  • It compresses traffic
  • It gives priority to new connections

FastTrack sends known connections down a fast lane that bypasses firewall, connection tracking updates and queues. On slide 279 it raised one TCP stream from 360 to 890 Mbps on an RB2011.

6. Why does the forward drop rule include connection-nat-state=!dstnat?
  • To drop port-forwarded traffic as well
  • To let in new connections that a dst-nat rule deliberately forwards
  • To disable masquerade
  • Because dstnat packets are always invalid

!dstnat means "not destination-NATed". New WAN connections are dropped unless a port forward sent them in on purpose.

7. With this lab's firewall, a friend on the Internet opens a game server you forwarded with dst-nat, while you browse the web. Which two packets are accepted by an established,related rule?
  • The web server's answers to your laptop
  • Your friend's first packet to the game server
  • The following packets of your friend's game connection
  • A stranger's first ping to your laptop

Answers and later packets of known connections are established. Your friend's first packet is new (let in by the dst-nat exception); a stranger's first packet to the LAN is new and dropped.

You now have a firewall in the spirit of MikroTik's default configuration, and you know why every line is there. Module 7 continues with queues: deciding not who gets in, but how fast each guest may move.