I took some time to play with this, and could of course, reproduce your finding. After some more tests I have the following hypothesis that might explain what you could observe:
As we know MikroTik has done a lot of work on VRF in RouterOS 7, even in recent versions, and my guess from what I can see, is that there is now a 1:1 mapping between VRF and routing table with FIB. We already know that when you create a VRF instance, a FIB routing table is dynamically added. We can also see that the main routing table actually has the associated main VRF that contains all interfaces.
But what if the reverse is also true. What if each time you create a FIB routing table, an implicit matching VRF instance is created in the background, it's just that this VRF contains no interfaces at all, and because it has no interfaces, the router also has no IP addresses in in this VRF.
There are several places in RouterOS where beside being able to specify @vrf you could also use @routingTable. Even right now in 7.20.4, tools like /ping and /tool/traceroute have the vrf= parameter, and when you press TAB after writing vrf=, you'll will only see the VRFs offered as completion. However, both those tools allow you to specify vrf=routingTable without an explicitly created VRF instance, and that work without problem.
(Note: in 7.21 those tools now no longer have the vrf= parameter but use the @ syntax)
If you consider that when writing new-routing-mark=xyz in action=mark-routing mangle rule, you are not specifying the routing table, but the VRF (including the implicit, hidden VRF) then what you observed can easily be explained. Let's look at the diagram from @jaclaz thread:
At #4 (mangle prerouting) you set the routing mark that choose the "VRF" mytable (implicit VRF), at step #12 a decision is made for dst-addr=self?. But, as I wrote above, in this implicitly created VRF mytable the router has no interfaces and no IP addresses. So, this check yields "N" and the next step in the flow is #13 (mangle forward), the INPUT chain is completely bypassed. The FORWARD chain will only send the packets to the out interfaces, even when you've explicitly added the dst-address=192.168.0.0/24 route, the gateway is the out interface bridgeLocal.
To give even more weight to the hypothesis, you can do the following experiment on your router:
-
We'll create a VRF. But because when you create VRF instances, you need to associate interfaces to them, first we have to add a dummy interface, let's create a dummy bridge:
/interface bridge
add name=dummy protocol-mode=none
We do not assign any IP address at the moment, to simulate the "router has no IP addresses" situation.
-
Add a mytable2 VRF instance containing that dummy bridge:
/ip vrf
add interfaces=dummy name=mytable2
As we know, this automatically adds the dynamic mytable2 FIB routing table.
-
Now we add the same default route that you added to mytable above, but to mytable2:
/ip route
add dst-address=0.0.0.0/0 gateway=vpn@main routing-table=mytable2
Note: the dst-address=192.168.0.0/24 route doesn't matter for this test, you can add it or not, there is no difference. I just write it here for completeness:
/ip route
add dst-address=192.168.0.0/24 gateway=bridgeLocal@main routing-table=mytable2
-
Of course, you also need to edit your mangle rule, so that it uses the routing mark mytable2 instead of mytable:
/ip firewall mangle
add chain=prerouting action=mark-routing new-routing-mark=mytable2 src-address=192.168.0.11
If you do the tests now, you'll see everything behaves exactly like your original configuration. Client 192.168.0.11 uses the vpn gateway to go to the internet. But is unable to access 192.168.0.1.
And the reason it's unable to access 192.168.0.1 is because in the mytable2 VRF, the router doesn't have the IP address 192.168.0.1. At step #12 of the flowchart, the comparison produces false. The packet will be sent to vpn (if you don't have the 2nd route) or to bridgeLocal (if you have the second route).
How can you make it works? Very easy, we just give the router the IP address 192.168.0.1 in the VRF mytable2!
/ip address
add address=192.168.0.1/32 interface=dummy network=192.168.0.0
Note that the netmask or network doesn't matter here, we just need the router to have the IP address 192.168.0.1 on one of the interfaces of the mytable2 VRF, in this case the dummy bridge is the perfect candidate interface.
Once this change is made, at step #12 of the flowchart, the check will produce true and the correct INPUT chain will be used (#6 mangle input is the next step). You'll see that 192.168.0.11 now has no problem accessing the router at 192.168.0.1 anymore.
I think if we accept that "creating FIB table creates implicit VRF with no interfaces" then the behavior you observed can be explained quite well.
As for a quick "fix" if you don't want to create VRF with dummy interfaces. Just add dst-address-type=!local to your original mangle rule. No extra routing rules needed.