How to keep CHR renewal when main VRF does not have internet access (but a different VRF has)?

My internet uplink is in a dedicated VRF (i.e., not in the default main VRF).

I have route leaking into main set up properly and hosts on interfaces which are in the main VRF can reach the internet without issues. However, RouterOS itself can't except I explicitly specify the VRF.

But I haven't found a setting to do this for license renewal (and neither have I seen it for software upgrade and /ip/cloud).

The latter ones I'd love to use but it's ok if they don't work at the moment since I can use my own DDNS and upgrade manually via Winbox.

But breaking my purchased license due to a RouterOS shortcoming itself is not acceptable.

Now I'm afraid I'll randomly be downgraded to just the min link speed.

Which option do I have to keep everything running without issues?

  • Any firewall/mangle hack?
  • Proxy server?
  • Offline renewal?

Currently it says next-renewal-at: 2026-06-24 17:58:13, deadline-at: 2026-07-24 17:58:13

That is not what is going to happen. When you have a PROPERLY licensed CHR (note that it is quite easy to inadvertently have a evaluation mode CHR that is not properly licensed!) it will not fall back to lower speed, but it will just refuse to upgrade to a newer RouterOS version.

After the deadline it will be in limited state, this state is indicated by the "limited upgrades" checkmark in the system->licenses window. It will still operate at the speed it was licensed.

I think with the information you gave it is a properly licensed CHR.

Depending on your configuration, you can probably "reverse" the VRF and main, i.e. have the WAN on main and LAN on a VRF.

@pe1chl : Ok that's some good news ... but, from then on it will never be able to upgrade again, correct? Not even if I am able to briefly renew manually?

@jaclaz No sadly not because there is always a restriction why something doesn't work. Sorry, it's really frustrating because literally, no matter what I do, the next step won't work because XYZ. In this particular case, route leaking via /routing/bgp/vpn does not work when main VRF is involved.

(Even if possible, there are multiple uplinks and I prefer having each of them in explicit VRFs)

PS: Also put in a support ticket with MT, hope they'll provide a solution.

Well, not "never be able to upgrade again". You can always fix the problem even when it is manually for only the renew.

The license renewal uses the server host license.mikrotik.com and you could put that one in an address list to allow outside traffic to that address, depending on how you have set up your configuration.

This trouble with special services is the main reason why I have never enabled VRF but instead use multiple routing tables and routing rules/marks that I configure manually. Of course that does not solve all problems either, but at least it is more flexible.

Yep. You can either address-list/mangle, or apply a not-too-subtle hack, creating a routing rule with interface=lo to redirect output traffic into a vrf.

The first has the inconvenience of having to correct the source address with src-nat and having to track the connection. The second one works more directly.

@pe1chl are you sure? I remember somewhere that after grace period a RouterOS install is broken for good and can never be upgraded again. Only option would be recreating VM and restoring backup

@lurker888 would you be able to share more details on a specific command(s)? I have spent hours without success and even the dirtiest hacks didn't work. But before, let me clarify that I can successfully leak routes (either via route leaking or route rule) into main VRF that works for other hosts. However, RouterOS itself never seems to work properly. And here let me also emphasize that the issue is the return packet: A return packet is received in the ISP VRF, not main; since it's a local destination packet, it will never be routed and hence never crosses a VRF boundary. Per my understanding, the kernel attaches to each process & packet which VRF it is in. Once a packet is received in VRF vrf-isp this VRF is attached to its packet and will kernel itself will only share it with processes that are attached to that same VRF. Hence no single route leak, route rule or NAT/mangle rule that I have tried did work.

That is why I wrote "When you have a PROPERLY licensed CHR"... it is possible to get a trial license and then convert that into a permanent license, when you make a mistake in that procedure you get the problem that you mention above. But when it is licensed correctly you see the two date fields updating regularly and then it won't do that.

I had it happen to one of my CHRs, I thought I had licensed it but it wasn't done properly. As usual you need to read and understand all the steps of the licensing, not assume it is done the way you expect it.

Then I also learned that it was very easy to just /export all the settings, setup a new VM and import the settings there and destroy the old one.

This is not totally correct. VRFs are a routing phenomenon and everything that effects routing effects them as well.

The basic method looks like this:

/ip firewall mangle add chain=output action=mark-connection dst-address-list=ip-license new-connection-mark=licensing passthrough=yes
/ip firewall mangle add chain=output action=mark-routing connection-mark=licensing new-routing-mark=my_target_vrf_table
/ip firewall mangle add chain=prerouting connection-mark=licensing new-routing-mark=main

The last rule allows the response packet to be routed through local.

You'll want to give your outgoing packet an ip address that is routable back to you:

/ip firewall nat add chain=srcnat action=src-nat connection-mark=licensing to-addresses=1.2.3.4

Of course, add the appropriate address list and make sure that dns works (or temporarily createa static entry.)

P.S. And make sure that the address of the licensing server is routable (not unreachable) in the main table. This just means that the route has to exist, and has to be active, not that it actually has to lead anywhere meaningful. The usual way to ensure this is to have a default in main, even if its just a route to an emtpy bridge.

Thank you, I really appreciate it. But I'm still not sure what I am missing (if any).

Given vrf-wwan with interface vlan2 (=internet uplink) and everything else in VRF main. I emphasize again that routing itself is correct and working because hosts from any other interface (that's in main VRF) can reach the internet without issues. Surprisingly even ping works. But LOCAL connections not (including /system/license and /system/package/update )

/ip/vrf/print 
Flags: X - DISABLED; * - BUILTIN 
 0    ;;; Internet uplink
      name="vrf-wwan" interfaces=vlan2 

 1  * name="main" interfaces=all 

/routing/rule/print 
Flags: X - DISABLED, I - INACTIVE; * - DEFAULT 
 0    ;;; main table
      action=lookup table=main min-prefix=0

 1    ;;; default: WWAN
      action=lookup table=vrf-wwan 

Note that for me the main table does NOT include a default gateway because I am using above policy instead. Having said that, I added a route in main for 159.148.147.251 (update.mikrotik.com) to vlan2@vrf-wwan, just in case.

NAT rule out of vlan2 is already set up:

/ip firewall nat add action=masquerade chain=srcnat comment="defconf: masquerade WAN interface" out-interface=vlan2

And then I added the rules suggested by you:

/ip firewall mangle add action=mark-connection chain=output comment="Access Mikrotik from main VRF" dst-address=159.148.147.251 new-connection-mark=mikrotik
/ip firewall mangle add action=mark-routing chain=output connection-mark=mikrotik new-routing-mark=vrf-wwan passthrough=no
/ip firewall mangle add action=mark-routing chain=prerouting connection-mark=mikrotik new-routing-mark=main passthrough=no

However:

[admin@router] > /system/package/update/check-for-updates
channel: stable
mode: https
check-certificate: yes
ip-version: ipv4
installed-version: 7.23.1
status: ERROR: connection timed out
[admin@SunGate2] > /ping 159.148.147.251
SEQ HOST                                     SIZE TTL TIME       STATUS
0 159.148.147.251                            56  45 161ms831us
1 159.148.147.251                            56  45 160ms756us
2 159.148.147.251                            56  45 161ms434us
sent=3 received=3 packet-loss=0% min-rtt=160ms756us avg-rtt=161ms340us max-rtt=161ms831us

[admin@router] >

(as I said above, ping works but none of TCP, UDP etc).

However, per packet sniffer, it can be clearly seen that the packet goes out and the return packet successfully comes back. In #6 it arrives back in vlan2:

Well, you're going out of your way to make it not work :slight_smile:

The approach relies on this:

When you add the route "159.148.147.251/32 via vlan2@vrf-wwan" (I'm guessing here), you force source address selection to select 135.180.131.119. (Source address selection happens at 16 in this chart The ultimate Mikrotik iptables flowchart) BUT: this address is not in local.

The intended packet flow is:

  1. source address selection happens according to a normal route and the selected address is in local (let's say 192.168.111.1)
  2. mangling occurs, redirecting the packet to vlan2-wwan (but the source address is not modified in the routing adjustment stage)
  3. postrouting src-nat occurs, making the packet signature (192.168.111.1->135.180.131.119)->159.148.147.251
  4. the response packet is translated back by connection tracking 159.148.147.251->(135.180.131.119->192.168.111.1)
  5. the packet is redirected by the mangle prerouting rule to main
  6. the address 192.168.111.1 exists in local
  7. the packet is received

So... by simply choosing any interface as the target for your route to 159.148.147.251/32 where the router has an address that is populated in local, you will succeed.

Just another thing to consider. I see (and I'm glad to see) that your packet capture is about the https connection. Ping works strangely, because ICMP reception happens a bit differently to other packets, and therefore using ping to troubleshoot issues regarding vrfs can quickly veer you off course.

This makes sense, before I explain why I think it still doesn't work, I believe I am doing is exactly what you are saying:

  • I created a dummy bridge TEMP, assigned IP 192.168.111.1/24 ; it's not assigned to a VRF, so it's part of main VRF.

  • In the "main" table I created a routing entry to this TEMP interface, making sure that the source address is selected as 192.168.111.1:

    /ip/route/print where dst-address=159.148.147.251
    Flags: A - ACTIVE; s - STATIC
    Columns: DST-ADDRESS, GATEWAY, ROUTING-TABLE, DISTANCE
    
    DST-ADDRESS         GATEWAY  ROUTING-TABLE  DISTANCE
    
    ;;; MIKROTIK
    0 As 159.148.147.251/32  TEMP     main                  1
    
  • I created the mangle rules which completes steps 1-2:

    /ip/firewall/mangle/print where comment~"Mikrotik"
    Flags: X - DISABLED, I - INVALID; D - DYNAMIC
    10    ;;; Access Mikrotik from main VRF: mark connection
    chain=output action=mark-connection new-connection-mark=mikrotik passthrough=yes dst-address=159.148.147.251 log=no log-prefix=""
    
    11    ;;; Access Mikrotik from main VRF: force routing to vrf-wwan
    chain=output action=mark-routing new-routing-mark=vrf-wwan passthrough=no connection-mark=mikrotik log=no log-prefix=""
    
    12    ;;; Access Mikrotik from main VRF: force return packet to main
    chain=prerouting action=mark-routing new-routing-mark=main passthrough=no connection-mark=mikrotik log=no log-prefix=""
    
  • I created an additional post routing src nat rule (even though one already exists for vlan2) for step 3:

    /ip/firewall/nat/print where to-addresses=192.168.254.1
    Flags: X - DISABLED, I - INVALID; D - DYNAMIC
    5 ;;; MIKROTIK
    chain=srcnat action=masquerade to-addresses=192.168.254.1 connection-mark=mikrotik log=no log-prefix=""

  • Steps 1-3 work as expected (per packet sniffer, received return packet and connection tracking)

  • Steps 4-7 also work (I strongly anticipate), yet connection fails:

Lastly, connection tracking confirms translation as requested:

Now why do I still think it doesn't work? It's not due to lack of addresses or routing tables, it's due to another thing: VRF labels that are on interfaces as well as processes.

Specifically, the /system/package/update/check-for-updates process is attached to VRF main only, it will not accept local packets that were received via VRF vrf-wwan.

I do not have a 100% conclusive answer for this but I am fairly confident because the similar behavior is true for Linux.

Have you tried above suggestion or is it based on assumption?

If my understanding is correct (hopefully not) this would imply that no routing/nat/mangle rule whatsoever can fix this. The only other hack I could come up is a tunnel that runs locally where one end terminates in main VRF, the other in vrf-wwan. This might be able to force treating the return packet not as local packet but instead forcing a routing decision. However, I have not been able to get anything successful with such a hack either.

Unfortunately, I think with the current state of RouterOS, no amount of mangle rules will be able to work around the issue.

First, as we've known since long, with the default policy rule chains, Routing Decision | RouterOS Manual, mangle rules have never been able to steer traffic to the router's local address in @main. Because the local chain is placed below mangle in the default list. That's why we normally have to include conditions such as dst-address-type=!local to exclude the local addresses from mangle policy routing. Thus this rule:

will not be able to resolve the destination of the return packets so that they land on the input chain, because the lookup needs to be done with the local table, and action=lookup, not action=mangle.

Second, although 7.22 allows us to define custom chains and place routing rules above the invisible action=mangle rule, following attempt also does not seem to work:

/routing rule
add action=lookup chain=highest table=local

/routing settings
set policy-rules=highest,mangle,vrf-lookup,vrf-unreach,local,user,main

Probably because the vrf condition of the rule is actually no:

image

So this rule placed in the highest chain will not match the response packets, because they have the vrf state set to yes.

If we attempt to modify the routing rule and turn on the vrf condition:

/routing rule
add action=lookup chain=highest table=local vrf

then it appears that this rule probably matches, however the table=local parameter is probably ignored, and the lookup is only done with the associated VRF routing table (as we know, the default hidden two rules for vrf don't set table at all:

) so the rule with vrf turned on will also not be able to steer to the local table.

Maybe we could rename the Mikrotik thing VSRF (Virtual Sometimes Routing and Forwarding)
:wink:
:rofl:

Well, as I already wrote above:

It of course partly is because of my lack of hands-on experience with VRF, but altogether it just seems too limiting. When you do not need overlapping IP space, it seems better to not use VRF at all.

Maybe OP can try to enable the CHR container /app inside the CHR itself, and use that free CHR instance (limited to 1Mbps) as relay router for upgrade/license check traffic.

  • Main CHR in has routes towards MikroTik servers in @main using the child CHR as gateway.
  • Traffic from child CHR towards internet is steered to the VRFs. This involves the forward chain, so no issue with mangle / routing rules.

This will consume a couple of hundreds MB RAM, and about 60MB storage.

Thanks for confirming @CGGXANNX . Still waiting for support answer from MT, hope they have a solution and will add vrf to the license ASAP. I mean out of all local services, this is the most important. And how come I am the only person in the world suffering from this?? :sleepy_face:

Thanks for your suggestion with CHR container. WOW this is wild but maybe hack I could do in the worst worst worst possible emergency option.

However, what do you think about my other comment?

Do you think this could work at all? I have played around with it but so far not with success. Tried creating two ipip interfaces (one in vrf-wwan, one in main) and one dummy bridge (in main VRF). Bridge had two IPs, local/remote of the ipip tunnels was set to those bridge IPs (but flipped) and an additional set of IPs assigned to the tunnel devices themselves.

MikroTik can probably easily add the setting option to select the VRF to Update Check / License / IP Cloud. They have added the option to choose the VRF to more and more RouterOS services with each update. And the "Check For Updates" menu recently gained a few more options already, so adding one more will not break the UI.


I've thought about your idea of the tunnel and came up with this implementation that appears to be working. The idea is to abuse MACVLAN interfaces for a light weight "tunnel". In this example, the VRF with WAN interface is called secondary.

  1. First, add a dummy bridge and two MACVLAN interfaces above it, with mode set to bridge:

    /interface bridge
    add name=vrf-link protocol-mode=none
    
    /interface macvlan
    add interface=vrf-link mode=bridge name=vrf-link-main
    add interface=vrf-link mode=bridge name=vrf-link-secondary
    
  2. Next, make sure vrf-link-secondary is added to the secondary VRF (do it with WinBox :stuck_out_tongue:).

  3. Add IP -> Address entries for the two MACVLAN interface:

    /ip address
    add address=172.18.0.20/24 interface=vrf-link-main network=172.18.0.0
    add address=172.18.0.21/24 interface=vrf-link-secondary network=172.18.0.0
    
  4. This is an important step, otherwise it won't work! Add a dummy route for destination 172.18.0.22/32 and a published ARP entry for that address on vrf-link-main:

    /ip route
    add dst-address=172.18.0.22/32 gateway=lo routing-table=main
    
    /ip arp
    add address=172.18.0.22 interface=vrf-link-main published=yes
    

    This will make sure that the router announces itself as recipient for destination 172.18.0.22 on vrf-link-main!

  5. Add two SRCNAT rules at the top of the NAT table, one for traffic coming out of vrf-link-main, and one for traffic coming out of the WAN interface with the source address 172.18.0.22:

    /ip firewall nat
    add action=src-nat chain=srcnat out-interface=vrf-link-main \
        to-addresses=172.18.0.22 place-before=0
    add action=masquerade chain=srcnat src-address=172.18.0.22 place-before=1
    
  6. Now we can add the route in @main that uses 172.18.0.21 as gateway. I am adding a default route here, but you can adjust the route(s) to match only MikroTik's address ranges.

    /ip route
    add dst-address=0.0.0.0/0 gateway=172.18.0.21 routing-table=main
    

With this setup, the router has internet access, at least for the Check For Upgrades functionality, although the only way to the internet is on the secondary VRF. It needs no mangle rules or routing rules at all.

In @main traffics to the internet will be redirected to the gateway at 172.18.0.21, which, to the router, looks like a host plugged to the vrf-link-main ethernet interface. So it will use ARP to find out the MAC address of that host on this interface. The router itself on vrf-link-secondary will answer this ARP request, with the MAC address of vrf-link-secondary.

Packets before being sent to 172.18.0.21, will be handled by SRCNAT, with the original source IP address replaced with 172.18.0.22. Packets will then go out of vrf-link-main.

The "remote" host is actually the router itself, on the interface vrf-link-secondary, which is in the secondary VRF. It sees the packets arriving from a host plugged to vrf-link-secondary, and doesn't know that the sender is actually itself.

The packets have the source 172.18.0.22 which is not an IP address assigned to the router! Packets will be forwarded to WAN (in secondary VRF). Before going out, the 2nd SRCNAT rule will masquerade the source address (no longer 172.18.0.22, but the address on the WAN interface).

Response packets arriving from WAN to the router will have that NAT-ing reverse, and the destination of the response packets will be changed to 172.18.0.22. We are still in VRF secondary.

The route to 172.18.0.22 in this VRF has the gateway set to vrf-link-secondary (which is one of the MACVLAN interfaces). Router will sends ARP on this interface to find out the remote MAC address belonging to 172.18.0.22.

Due to the published ARP entry and the /32 route from step #4 above, the router will announce its MAC address on vrf-link-main as the one for 172.18.0.22.

Packets with destination 172.18.0.22 are sent out of vrf-link-secondary and arrive at vrf-link-main. Once here, SRCNAT (the first rule) will be reversed, changing the destination address 172.18.0.22 back to the original sender IP address in @main.


We cannot get rid of the 172.18.0.22 intermediate address. If we use 172.18.0.20 in the NAT rules (the address on vrf-link-main), the reverse SRCNAT-ing will not work (probably because the router sees 172.18.0.20 as one of its own addresses in @main). Maybe this is the reason why your tests with tunnels didn't work. Because your tunnels have the two ends both being the router's IP addresses.

wow. WOW! You are a hero!!

I would have never come up with that.

The trick with the 172.18.0.22/32 is so ingenious that somehow I missed it ... because I thought it's taken care of by the /24 route. Still took my an hour to get it working.

Thank you so much ... not just for proposing it but also trying it and writing all the necessary commands.

Glad that it works! I did some test, and step #4 can be further simplified. Instead of adding the published ARP entry and the 172.18.0.22/32 route using the lo interface as gateway, we can replace those two with a single static APR entry:

  1. We look at the details of the vrf-link-main MACVLAN interface to see what MAC address is assigned to it:

    And just add a single static ARP entry with that MAC address on vrf-link-secondary:

    /ip arp
    add address=172.18.0.22 interface=vrf-link-secondary mac-address=FE:43:8A:88:5F:A1
    

This also saves an ARP-broadcast because vrf-link-secondary no longer needs to send ARP to find out who is 172.18.0.22 anymore.

In fact, because the MACVLAN interfaces both have static hardcoded MAC addresses, we can do the same for the other direction. A static ARP entry for 172.18.0.21 on vrf-link-main, with the MAC address of vrf-link-secondary can be added, so that vrf-link-main no longer needs to send ARP to discover 172.18.0.21.