Local services unreachable via VRF interface: packets trapped in FORWARD chain

Setup:
An external interface is assigned to a specific VRF (e.g., "VRF_ISP").
/ip vrf
add interfaces=ether2 name=VRF_ISP
Local services (such as L2TP) services are running in the main routing table context.
Ip address exist in both main and VRF_ISP
/ip address
add address=10.0.0.2/24 interface=ether2 network=10.0.0.0
add address=10.0.0.2/32 interface=lo network=10.0.0.2

The Issue:
Local services unreachable via VRF
To resolve this, I have tried using /routing rule or /ip firewall mangle to force a lookup in the main table:
/ip firewall mangle
add action=mark-routing chain=prerouting disabled=yes dst-port=1701 in-interface=ether2 new-routing-mark=main protocol=udp
/routing rule
add action=lookup-only-in-table disabled=no dst-address=10.0.0.2/32 routing-mark=ISP table=main
Incoming packets for local services (e.g., UDP 1701 for L2TP) arriving at the VRF interface fail to reach the INPUT chain. Instead, they are processed in the FORWARD chain.
Observation:
Even after the routing table is changed to main, the router does not recognize the packet as "Local Delivery".
Firewall logs confirm that these packets bypass the INPUT chain entirely and are handled as transit traffic in the FORWARD chain.
/ip firewall mangle
add action=accept chain=input dst-port=1701 log=yes protocol=udp
add action=accept chain=forward dst-port=1701 log=yes protocol=udp
ip firewall/mangle/print stats
Flags: X - DISABLED
Columns: CHAIN, ACTION, BYTES, PACKETS

CHAIN ACTION BYTES PACKETS

0 prerouting mark-routing 744 6
1 input accept 0 0
2 forward accept 744 6

Questions:
Why does the Routing Decision process treat packets destined for the router's own IP as transit traffic (Forward) when they originate from a VRF interface, even after a manual table lookup shift to main?
Is this behavior expected in the current v7.14+ architecture, or is it a known limitation of the VRF implementation?
Are there plans to make the L2TP server "VRF-aware" so it can listen directly on interfaces within a VRF without requiring workarounds like VETH or physical loopback cables?

What you observe is explained by this summary from answers provided by MikroTik Support:

Note: the current development version 7.22beta1 already exposes the 5 default routing rules that you see posted in that post. You can also move them and add new rules with those new actions too.

In short: Your mangle mark-routing rule overrides the other default routing rule actions below it (routing rule with action=mangle is first). If the packet is matched by a mangle mark-routing rule, then the action=lookup table=local rule will no longer be reached. But in the default list that rule is the only spot where the routing decides whether the router is the receiving end of the packet (by comparing the destination address with the list of local addresses of the router / router's own addresses like you called it).

If you have mangle mark-routing that is hit, then it doesn't matter that the destination address is an address of the router, the routes in the routing tables will be used to look up routes to forward the packets only.

Starting from 7.22beta1, if you want that packet to hit the input chain, you'll need to manipulate the routing rule table, for example by adding this at the top, before the action=mangle default rule:

/routing rule
add action=lookup dst-address=10.0.0.2/32 table=local place-before=0

You can also assign other addresses to lo like 10.20.30.40 and have a dst-nat rule that redirects 10.0.0.2 to 10.20.30.40, and have the above routing rule be

/routing rule
add action=lookup dst-address=10.20.30.40/32 table=local place-before=0

And it will have the same effect. If you try to ping 10.0.0.2 from the VRF while having the DSTNAT rule, you'll see that ping doesn't work unless you enable that routing rule.

However, while that places the packet in the correct input chain instead of the forward chain, what happens next depends on the listening service on the router. I haven't tested L2TP, but while ping works, other service that have explicit VRF setting like www, www-ssl, ssh, dns, winbox, v.v... will reject the connection attempt (by replying to SYN with ACK,RST) probably because the VRF doesn't match the setting.

So, while I can explain to you why the packets land in the forward chain (because mangle causes local address lookup to be skipped) and there is a way to put the packet in the input chain with the latest beta, there is no guarantee that the router process that receives the incoming connection will accept it.

I think this is very probable. MikroTik has recently add VRF supports to more and more services, the latest being WireGuard.

Thank you very much for the detailed explanation; it finally makes sense why this is happening. I have updated to v7.22beta and performed some experiments based on the new routing rule capabilities.
My goal was to "catch" the packet, move it to the main table, and then force it into the local table to reach the Input chain.
My configuration:

Mangle rule to move traffic to the main table:
/ip firewall mangle add action=mark-routing chain=prerouting dst-port=1701 in-interface=ether2 new-routing-mark=main protocol=udp
Routing rule to force the delivery from main to local:
/routing/rule/add dst-address=10.0.0.2 routing-mark=main action=lookup-only-in-table table=local place-before=0

Results:
The firewall logs confirm that the traffic now successfully hits the INPUT chain:
input: in:VRF_ISP out:(unknown 0), connection-mark:ISP connection-state:new src-mac 08:00:27:D7:14:9F, proto UDP, 10.0.0.1:1701->10.0.0.2:1701, len 124
However, the service (L2TP) still doesn't "see" the packet. Instead, the router immediately replies with an ICMP Destination Unreachable (Port Unreachable):
output: in:(unknown 0) out:VRF_ISP, connection-mark:ISP connection-state:related proto ICMP (type 3, code 3), 10.0.0.2->10.0.0.1, len 152
Conclusion:
It seems that even though the routing table is changed and the packet reaches the Input chain, the internal binding to the VRF interface (in:VRF_ISP) persists within the packet metadata. Since the L2TP service socket is not VRF-aware, the kernel drops the packet before it reaches the application layer, resulting in the ICMP Type 3 Code 3 response.
It appears that without making the service itself "VRF-aware," even the new routing rules in 7.22 cannot bridge this gap.

1 Like