The attraction is to be able to use overlapping address spaces handled by a single router and/or having an individual routing for different customers configured more easily than using the whole suite of policy routing settings.
Let’s limit the example to routing inside overlapping address spaces.
Imagine four networks - A, B, C and D. A and B use identical subnets, and so do C and D. On your router, one interface is used to connect each of the four networks, and assigned an IP address from the corresponding subnet. Therefore, the system dynamically creates routes to these connected subnets, and choses between them in an unpredictable way (it’s not random, it’s just unpredictable, so always one of the identical subnets in each pair will be used but you cannot know in advance which one it will be).
What you want is to make sure that a packet from network A with destination address matching both C and D will always go to C, while a packet from network B to the same destination address will always go to D.
With “manual” policy routing, you have to create mangle rules to assign a routing mark “AC” to packets coming in via interfaces to which network A and network C are connected, and a routing mark “BD” to packets coming in via interfaces to which network B and network D are connected. Also, you have to manually create routes to subnet A and subnet C with routing mark AC, and routes to subnet B and subnet D with routing mark BD, to override the dynamically created routes as described above.
As a result, when a packet towards a “C or D” address comes in via interface A, it gets a routing mark AC, so only the route to C becomes valid for it although the dynamically generated route to D has the same dst-address; likewise, a packet towards a “C or D” address coming in via interface B gets a routing mark BD and so only the BD-marked route to D becomes eligible for it.
Now if one of the interfaces went down, the marked route for its subnet would become inaccessible, so the routing would fall back to the dynamically created route without any routing mark, so the packet would end up in a wrong destination network. To prevent this, you would have to use two /ip route rule items saying routing-mark=X action=lookup-only-in-table table=X (one with X=AC and the other one with X=BD).
So in total, it takes:
- two mangle rules referring to two interface lists with two member interfaces each
- four marked routes
- two routing rules
Using the vrf configuration, you use just two configuration items to define the two virtual routing instances instead:
/ip route vrf
add interfaces=A,C routing-mark=AC
add interfaces=B,D routing-mark=BD
These guarantee that:
- the dynamically created routes to the connected subnets A,B,C,D will be (re)created with a routing-mark AC and BD respectively instead of being created in the main routing table (so no need to manually create routes to A, B, C, D subnets and to use routing rules to prevent fallback to identical routes in main routing table in case of interface being down,
- packets entering through any of the interfaces A, B, C, D will be assigned the corresponding routing-mark automatically, so no need to create the mangle rules.
So things are much simpler to set up and stay orderly if you take the vrf way in some scenarios. But it has some limitations - the same routing tables/routing marks are used for both the vrf and for the “usual” policy routing, so if you would want to use the “usual” policy routing together with a vrf setup, your brain may quickly reach the boiling point trying to keep track with the mutual dependencies.
Another use out of the scope of the example above is, as mentioned in previous posts, the possibility to update each vrf-created routing table independently by a dedicated instance of a dynamic routing protocol.