Connection tracking, connection-state=invalid and loose-tcp-tracking

I’m doing a yearly review of the firewall and would like to address an issue with dropped outgoing invalid packets.

My current firewall setup for IPv4:

> /ip/firewall/filter/print where chain=forward
chain=forward action=fasttrack-connection hw-offload=yes connection-state=established,related
chain=forward action=accept connection-state=established,related
chain=forward action=jump jump-target=trap out-interface=trap # unassigned private-use IPv4 identified via routing rules 
chain=forward action=jump jump-target=invalid connection-state=invalid
chain=forward action=accept connection-state=untracked
chain=forward action=reject reject-with=icmp-admin-prohibited in-interface-list=LAN
chain=forward action=drop

> /ip/firewall/filter/print where chain=invalid
chain=invalid action=accept tcp-flags=rst protocol=tcp in-interface-list=LAN
chain=invalid action=accept tcp-flags=fin protocol=tcp in-interface-list=LAN
chain=invalid action=reject reject-with=tcp-reset protocol=tcp in-interface-list=LAN
chain=invalid action=reject reject-with=icmp-admin-prohibited in-interface-list=LAN
chain=invalid action=drop

and for IPv6:

> /ipv6/firewall/filter/print where chain=forward
chain=forward action=fasttrack-connection connection-state=established,related
chain=forward action=accept connection-state=established,related
chain=forward action=jump jump-target=trap out-interface=trap # unassigned private-use and delegated IPv6 identified via routing rules 
chain=forward action=jump jump-target=invalid connection-state=invalid
chain=forward action=accept connection-state=untracked
chain=forward action=reject reject-with=icmp-admin-prohibited in-interface-list=LAN
chain=forward action=drop

> /ipv6/firewall/filter/print where chain=invalid
chain=invalid action=accept protocol=tcp tcp-flags=rst in-interface-list=LAN
chain=invalid action=accept protocol=tcp tcp-flags=fin in-interface-list=LAN
chain=invalid action=reject reject-with=tcp-reset protocol=tcp in-interface-list=LAN
chain=invalid action=reject reject-with=icmp-admin-prohibited in-interface-list=LAN
chain=invalid action=drop

Some of my hosts have TCP connections that somehow end up being unknown to RouterOS’s connection tracking. They also seem to ignore tcp-reset and do not re-establish connections. Or perhaps they are coded to bypass proper TCP handshake. Some of these connections seem to be related to Apple’s iCloud Private Relay (ODoH):

2025-04-28 16:52:46 firewall,info drop6-lan invalid: in:vlan-main out:ether1-gateway, connection-state:invalid src-mac ..., proto TCP (ACK,PSH), [...]:63124->[2620:149:a43:200::9]:443, len 67

Nothing obvious is broken but I would like to address this nevertheless.

I plan to selectively allow some of the invalid packets but would like to reaffirm that I properly understand connection tracking in RouterOS. Please let me know whether the following statements are valid:

  1. loose-tcp-tracking=yes only applies to SYN,ACK and ACK packets
  2. loose-tcp-tracking=yes only applies to ACK in response to SYN,ACK which was seen by the firewall. ACKs that do not follow up seen SYN,ACK (data ACKs) or without matching sequence number are not subject to this setting
  3. A connection-state=invalid packet remains “invalid” and does not transition to any other state nor registers new connection in connection tracking even if accepted

For testing, I modified firewall rules for both IPv4 and IPv6 to accept all outgoing connection-state=invalid packets. Now I see this:

2025-04-28 18:48:51 firewall,info accept6-lan invalid: in:vlan-main out:ether1-gateway, connection-state:invalid src-mac 08:87:c7:37:b0:5b, proto TCP (ACK,PSH), [...]:63270->[2620:149:a43:20a::4]:443, len 67 
2025-04-28 18:48:51 firewall,info drop6 invalid: in:ether1-gateway out:vlan-main, connection-state:invalid src-mac c4:ca:2b:5a:19:97, proto TCP (RST), [2620:149:a43:20a::4]:443->[...]:63270, len 20

As you can see now that the internet endpoint can receive packets from my host it rejects with RST. Which suggests that action=reject reject-with=tcp-reset was the right call. I wonder why RouterOS’s tcp-reset was not enough though. Also I wonder how can I let this reply through without accepting all invalid packets in the forward chain unconditionally…

Looked a bit more and it seems the host is to blame: it attempts to send data after acknowledging server’s FIN,ACK. The RouterOS does the right thing by sending RST and the client acknowledges it. Perhaps it has to do something with power conservation.

The only adjustment I think to make is tcp-flags=fintcp-flags=fin,ack as with the packets already being invalid only the fin,ack ↔ ack connection termination is possible without retransmission errors.

Great info here. Like the use of jump and using the “RFC ways” to terminate connection, instead of just “drop”.

I’ve never dug into “invalid” too much, so IDK here. But I suspect you’re right about “it’s the host”…so many tricks added to TCP stacks over the years, and not all play exactly by the rules.

One thing on “invalid”, you might want to look at the “input” and “output” chains. While I cannot say recently, but historically RouterOS itself (like “local processes” in Packet Flow) can induce “invalid” state in some cases. If you’re using IPSec, that’s something to look at in your “annual firewall review”…it may be covered in your “jump scheme” but IPSec gets complex in packet flow, so easy to have some issue there with custom firewall rules.

I found it oh so much easier to troubleshoot lan with proper errors :slight_smile:

Agreed the state machine for a tcp connections is quite sophisticated and troubleshooting tools are lagging behind.

Ended up adjusting both IPv4 and IPv6 filters to accept both invalid RST and FIN,ACK regardless of the origin:

> /ipv6/firewall/filter/export where chain=invalid
add action=accept chain=invalid protocol=tcp tcp-flags=rst
add action=accept chain=invalid protocol=tcp tcp-flags=fin,ack
add action=reject chain=invalid in-interface-list=LAN protocol=tcp reject-with=tcp-reset
add action=reject chain=invalid in-interface-list=LAN reject-with=icmp-admin-prohibited
add action=drop chain=invalid

> /ip/dns> /ip/firewall/filter/export where chain=invalid
add action=accept chain=invalid protocol=tcp tcp-flags=rst
add action=accept chain=invalid protocol=tcp tcp-flags=fin,ack
add action=reject chain=invalid in-interface-list=LAN protocol=tcp reject-with=tcp-reset
add action=reject chain=invalid in-interface-list=LAN reject-with=icmp-admin-prohibited
add action=drop chain=invalid

The reasoning being that I trust both upstream and LAN enough as well as consider the chance and impact of an RST-attack as low. Not a big price for improved connection termination.

Still, would love to get confirmation wrt statements in the OP.



Some of these connections seem to be related to Apple’s iCloud Private Relay (ODoH):

IDK, but Apple does like TCP multiplath … so perhaps related to escaping “invalid” you commented on.


I guess I’d say what is “new” is actually what loose-tcp-tracking=yes/no controls… And from my [re-]reading the conntrack doc… you can get a “new” stat from just SYN,ACK (and ACK back) with =yes. With =no meaning you need the full SYN → SYN,ACK → ACK to flag a “new” state. In either case, once a “new” state being detected that starts the conntrack. I don’t think it too smart, and matches what’s described.

“invalid” is just the result if state is it’s not new nor already in conntrack (nor, untracked by RAW). I’ve always thought of “invalid” as “none of the above” … so “invalid” is always untracked AFAIK – but never explicitly tested — but I think it have to be not in conntrack to give opportunity for detection of new “new” state on future packets from same src/dst pair.

And the TCP dance with MP-TCP I’m even less familiar, but Apple Siri at least uses and other Apple things too. So how the tcp-reset stuff works in MP-TCP flows, IDK, but should be transparent AFAIK. Perhaps not too.

I doubt TCP-MP is involved: the device is a laptop and WiFi was its only path to the internet. I only have one gateway in the network.

I donated my copy of TCP/IP Illustrated long ago. And Mikrotik docs are a bit vague. So very hard to definitive. Maybe someone else has ideas / double-check your theory.

Fair enough. Just a thought. And, you’d likely would have already seen an issue if TCPMP related.