Assign (wireguard) interface local ip route to specific routing table

Is it possible with RouterOS to automatically assign the route for a wireguard interface to a specific routing table? I know you can add a lookup-only-in-table routing rules etc but then the client ip address of the wireguard interface is still added to the main routing table. But due to an ip conflict, I don’t want that client ip address being added to my main routing table.

Under plain linux you can add a if-down/up network script to change f.e. routes whenever an interface changes state, but afaik routeros scripting doesn’t support interface events like that?

Instead of presupposing the solution stating the issue solely and asking for potential approaches is better.


To be clear
a. who decided the IP address schema of the wireguard subnet and can you change it?
b. who decided the IP address schema of the local subnet that clashes and can you change it.

The quick answer is NO, the router automatically creates a route
add dst-address=wireguardsubnet gateway=wireguard interface routing-table=main.

However, RoS is flexible and there may be other approaches to deal with the issue.
More clarity on detail may assist.

Neither subnets can be changed unfortunately, and even if I could i still wanna understand why wireguard on routeros requires an explicit client route in the main table while wireguard on linux doesnt seem to require that :slight_smile:

Also the other subnet is not a local subnet but a remote subnet. To explain the problem in terms of routes, currently have the following:

Dst. Address	Gateway		Distance	Routing Table	Contribution
0.0.0.0/0	192.168.6.1	1		via-wireguard	active
192.168.6.0/24	%if		10		main		active
192.168.6.0/30	wg-remote	0		main 		active

With these routes I can use route rules or routing marks to route traffic over the wireguard vpn. But it also means I cannot reach 192.168.6.1 t/m 3 on %if cause they overlap and wg-remote has a lower distance. Have tried setting the first route to the interface name wg-remote instead of its ip address, but then the route becomes unreachable.

What routers or devices are handling wireguard at each end?

Don’t know, why could that matter for the explicit client route in the main table on my mikrotik?

Maybe I explained it not clear enough, but Im connecting with a linux machine to the same wireguard network and on linux there is no route added for the wireguard client ip to the main table. So running ip r only shows the %if subnet, nothing from the wireguard if.

There may be some tricks you can do with NAT ( source or destination ) but this assumed two mikrotiks at either end.
Also its not clear whether or not the duplication is the subnet at your router, with wireguard, OR with the remote subnet at the other end, with wireguard?

Lets assume the duplication is at your end and we make up a faux subnet to use…for wireguard traffic
/ip firewall nat
add chain=srcnat action=src-nat src-address=DuplicateSubnet out-interface=wireguard-interface to-address=10.20.30.0/24

_/ip route
add dst-address=10.20.30.0/24 gateway=wireguard-interface routing-table=main[/i

/wireguard peers
add allowed IPs=wireguard-subnet,10.20.30.0/24 …

In this way any request from your local subnet would go out with a different IP address as source and it would be routed out wireguard.
At the other end,
the source of the request would appear to be from 10.20.30.1 etc…
Would need commensurate firewall rules etc…

++++++++++++++++++++++++++++++++

If you need separate routing tables, Mikrotik has the VRF functionality, and one could assign the wireguard interface to a VRF, each VRF has its own routing table, so that would match closest to what your used to doing in linux …

https://help.mikrotik.com/docs/spaces/ROS/pages/328206/Virtual+Routing+and+Forwarding+-+VRF_

Ok thanks, it’s sad this can’t be just fixed with normal routing tables. Maybe I will contact support if I have time to create a minimal repro

Yes it can, use VRF to create the additional virtual routing table on the mikrotik device!!

Yeah ok, but I said normal routing tables :wink:

VRF is not really ‘normal’ and complicates things like firewall. Not sure how routing tables & vrf tables correlate in RouterOS, but it seems to me that the only thing we’d need to be able to do to fix this with normal routing tables is to either have a routing-table dropdown in /ip/address OR have an script event for when an interfaces comes up/down (ie disabled/enabled)

Its RoS, not linux, sorry. VRF will work for your requirements.

Maybe too many misunderstandings here?

Mikrotik only adds a route via Wireguard interface automatically (to any routing table) if you attach a subnet to the Wireguard interface. There is no need to do it, though - it is just the simplest setup for the most typical use case where one wants a bunch of clients to connect and use /32 addresses from that subnet. If that does not suit your use case, just do not attach any subnet to the Wireguard interface, and RouterOS will not add any route via that interface automatically, allowing you to decide which destinations you want to use the interface for and using which routing table.

Making the WG interface a member of a VRF is an easy way how to separate the traffic to/from the WG interface from any other traffic - if you do that and attach a subnet to the interface, a route to that subnet will be added to the routing table of that VRF.

Thanks @sindy!


just do not attach any subnet to the Wireguard interface, and RouterOS will not add any route via that interface automatically, allowing you to decide which destinations you want to use the interface for and using which routing table

Do you maybe have an example on how to do that? I tried adding a route using the interface as gateway (instead of the .1/30 ip address), but then the 0.0.0.0/0 wg-remote 1 via-wireguard route becomes unreachable as soon as I try to use it.

That typically happens when check-gateway is set to anything else than none on a route whose gateway is not an IP address. Could it be your case here?

Yeah, that’s true but there is also no traffic being routed over the wg-remote interface if I set the gateway for the 0.0.0.0/0 route to the interface name.

Also when I log traffic in mangle prerouting & postrouting rules, then no postrouting entry is ever logged when setting the gateway to the interface name (they are when setting the gateway to an ip address). I guess this means that routeros just doesn’t know where to send the traffic to if you use the interface name as gateway? Cause otherwise it would have logged a postrouting entry.

Currently my config in pseudo code is:

  • wg-remote interface
  • bridge1 with dhcp-server etc
  • via-wireguard routing table
  • lookup-only-in-table routing rules for both wg-remote & bridge1
  • src-nat masquerade rule in NAT firewall for traffic going out on interface wg-remote

What i also tried:

  • No ip address assigned to wg-remote as to not add a route in the main table
  • Manual 192.168.6.0/30 route in via-wireguard routing table
  • 0.0.0.0/0 route in via-wireguard table with gateway set to 192.168.6.1
  • client ip set to 192.168.6.2 in wireguard config for wg-remote

but this doesnt work either, the gateway stays unreachable and no traffic is being routed

Rather than the pseudo-code, post the actual config. It normally works so there must be some minor mistake somewhere. As you mention the bridge name in the same row of the pseudocode like the routing rule, I suppose you are ping from the outside, not from the Mikrotik itself?

I think the confusion comes about because OP never actually describes what they want to do.

Btw. assigning a default via a wg tunnel in a table of your choice works perfectly. Just tried it, for me it shows up as “As” (Active, static) and doesn’t have the U (Unreachable) flag, which is quite logical for an interface gw and given the fast that wg tunnels are always up. Routing to it also works correctly.

Mikrotik’s routing works exactly as Linux routing does, with only some exceptions for things that are not or not yet implemented, so whatever works well in Linux usually can be translated quite easily to Mikrotik-speak. The differences are only naming, organizational, etc. The things that are not implemented are usually only required for fairly advanced setups. This whole thing does not apply to dynamic routing - the routing daemon/daemons are entirely Mikrotik internal products.

From your attempts at configuration I would venture to guess that you have the following setup and want the arrangement like this:

  • You have a remote network 192.168.0.0/24, that has a router on it 192.168.0.1, that is the default gateway for the internet on the remote side
  • You want to have a network locally - let’s say - 192.168.200.0/24, where your local router would be 192.168.200.1, would be the default gateway, and hand out addresses by dhcp to local clients
  • You want you router to participate in the remote network as 192.168.0.2
  • You want your local side clients to be able to access the remote side network 192.168.0.0/24 src-natted (masqueraded) to 192.168.0.2
  • You want your local side clients to able to access the Internet via the remote-side network (via its gateway) src-natted to 192.168.0.2

Please underline/correct as appropriate :slight_smile:

If this is indeed your use case, it is a fairly standard/simple setup.

I just don’t know where the “client route” (an expression I am not familiar with in this context) comes in. Why /30? Yes, a /30 has its broadcast address at .3 in this case, this will of course mask any other address. This is not surprising. The masking btw. happens regardless of distance because of the shorter prefix.

To your original question more specifically: if you add an address to an interface, then of course it prompts route addition. The route is added to the routing table that the netdev is bound to (in Linux speak). This is the exact same way in Linux and in Mikrotik. I really don’t see where the problem comes in; you will probably want to use a separate (non-main) table for both you wireguard (in-tunnel) traffic *and * your local bridge. Bind both these netdevs (interfaces) to this second table, and you won’t have to mess around with routing rules, markings, etc., lookups will work naturally both in the main and secondary tables.

After checking everything again it seems to work now with these two caveats:

  • The gateway check in the 0.0.0.0/0 route with the interface name as gateway needs to be none/disabled. Cause any other option means that the route will work for 5s or so, then ROS checks the gateway but fails twice and marks the route as unreachable. Am not sure why the gateway check fails. But basically what @sindy said, just took me a while to realize/understand why he made that remark.
  • You cannot use a src-nat/masquerade rule, you have to use a src-nat/src-nat rule and set the ip address to the client ip address of the wg interface

@lurker888 If you edit a wireguard peer in winbox, then the bottom section is client address/dns/endpoint etc. Thats what I was referring to.
To answer your other questions, I have a 192.168.6.0/24 subnet and a wireguard interface that uses 192.168.6.2 as local address and 192.168.6.1 as remote address. So i thought that I had to use a /30 on the mikrotik router to support routing, but as y’all have shown me that was a wrong assumption. So now there is no issue anymore with overlapping subnets :slight_smile:

Relevant configuration is now:

/interface bridge
add name=bridge-wg
/interface wireguard
add listen-port=27354 mtu=1420 name=wg-remote
/ip pool
add name=pool-wg ranges=192.168.0.10-192.168.0.250
/ip dhcp-server
add address-pool=pool-wg authoritative=after-2sec-delay interface=\
    bridge-wg lease-time=10m name=server-wg
/routing table
add disabled=no fib name=via-wireguard
/interface wireguard peers
add allowed-address=0.0.0.0/0 client-address=192.168.0.2/30 client-dns=\
    192.168.0.1 endpoint-address=1.2.3.4 endpoint-port=51820 interface=\
    wg-remote name=wg-remote-peer persistent-keepalive=25s
/ip address
add address=192.168.0.1/24 interface=bridge-wg network=192.168.0.0
/ip dhcp-server network
add address=192.168.0.0/24 comment=wg dns-server=192.168.0.1 gateway=192.168.0.1 \
    ntp-server=192.168.0.1
/ip firewall nat
add action=src-nat chain=srcnat log=yes \
    log-prefix=WG out-interface=wg-remote to-addresses=192.168.0.2
/ip route
add disabled=no distance=1 dst-address=0.0.0.0/0 gateway=wg-remote \
    routing-table=via-remote scope=30 suppress-hw-offload=no target-scope=10
/routing rule
add action=lookup-only-in-table disabled=no interface=wg-remote table=\
    via-remote
add action=lookup-only-in-table disabled=no interface=bridge-wg table=\
    via-remote

Thanks for your time & patience y’all, lets consider this solved! :smiley:

Because the gateway check is done literally the way chosen. So if you choose ping, the gateway must be set to an IP address - if it is not, there is nothing to be pinged. RouterOS doesn’t make this plausibility check and simply returns a failure for the ping rather than ignoring the check-gateway setting in such case. arp has the same issue.

Specifically in case of Wireguard, it makes no sense to use check-gateway because the Wireguard interface is basically a gateway from the “big” router to the “small” one that routes the traffic to different peers, matchiing the destination addresses of the packets against the peers’ i]allowed-address[/i] parameters. So some destinations may be reachable and some not, depening on the peer connections currently up. I wonder why this mistake (using check-gateway=ping for routes with gateway=wgX) became so frequent during the past few months, i.e. which forum post was the “patient 0”.


That is also not surprising given that action=masquerade sets the reply-dst-address of the connection to the primary address assigned to the outgoing interface of the initial packet of that connection, but since no such address exists in this particular case, action=masquerade cannot do anything. action=src-nat chooses the reply-dst-address from the pool specified in the to-addresses parameter, which can be even totally unrelated to any local address of the router.

I cant speak for others, but I guess my ‘confusion’ comes from that when setting up a wireguard interface (either in linux or ros) you specify the local interface ip. See f.e. here where they set 192.168.2.1 as local ip and 192.168.2.2 as peer ip : https://www.wireguard.com/quickstart/#command-line-interface And because you have a local interface and remote ip address, I expected to be able to ping between them.
What I also tried was setting the gateway to 192.168.6.1%wg-remote with a preferred source of 192.168.6.2. Of course I didnt give this much chance to work, but was hoping that ROS would be so flexible that I could instruct ROS this way how it should execute the check-gateway=ping. But as expected the % syntax doesnt work for manually added routes :wink:

Also I do think that in general it is useful to have a gateway check on a wireguard route as wireguard connections do not have a clear connected state. Instead it’s basically just a key exchange timeout that needs to occur before a wireguard connection is considered disconnected.


On the one hand I agree with you that this seems logical, hence why I realized this could be a potential issue. But on the other hand, what is the use of specifying a client ip address on a wireguard peer if ROS doesn’t use it everywhere it should/could? That configuration exists for a reason right? And logically speaking, if the client ip is specified then ROS could used it for the masquerade rule too.

So I do think that the above two issues could be considered feature requests / possible improvements for ROS :wink:

In “normal” case where the wireguard is part of the “default VRF”, you can indeed use the Wireguard interface similarly to an Ethernet one in terms that you attach an address from some subnet to it and can use other addresses from that subnet as gateways for some routes. So if you have e.g. three Wireguard peers, you assign each of them one address from the subnet attached to the Wireguard interface and put that address to the allowed-address list for that peer along with all the other destination prefixes that are reachable through that peer. And then you use that /32 address of the peer as the gateway of routes to those prefixes and use check-gateway=ping to monitor the actual transparency of the tunnel.

But you wanted to handle the routing to/from that Wireguard interface using some other-than-main routing table but at the same time share the IP address space with it (because you wanted to avoid using a non-default VRF). You can put manually configured routes to any table you want, but those that are dynamically created by attaching a subnet to an interface are always put into the routing table of the VRF that contains that interface. So if you keep the Wireguard interface in the default VRF and attach a subnet to it, a route to that subnet will pop up in routing table main.

You probably could attach no subnet to the Wireguard interface, manually add a route to the remote peer’s internal /32 address with the Wireguard interface as a gateway, and add another route to the actual destination with that /32 address as a gateway using the recursive next-hop search:
/ip route add dst-address=10.10.10.3/32 routing-table=via-WG gateway=wg-remote@via-WG scope=11
/ip route add dst-address=10.20.0.0/16 routing-table=via-WG gateway=10.10.10.3/32@via-WG target-scope=12 check-gateway=ping

But it is just a rough idea, I haven’t tested it.


Wireguard does not require that a remote peer had an own internal address. Wireguard provides the tunnel to a specified peer, sends to it what you tell it to send there using a list of prefixes, and hands over to you what comes from there if it matches the same list of prefixes. Whether you assign the peer any own address on top of the other ones it already has and whether you use that extra address for anything in your local context is purely up to you. Making this a default behavior would require adding some knobs and levers to allow the admin to disable it, otherwise the flexibility would be reduced, and it is more logical to let you set up what you want than to let you disable what you do not want.

Besides, I have no idea how a (remote) peer address is related to a (local) masquerade.