>
Home Automation

Why your MikroTik wireguard tunnel can ping the peer but not the LAN

There is a flavor of network problem that only shows up when the tunnel comes up cleanly. You can ping the WireGuard endpoint (a software-only virtual private network that runs as a kernel module on Linux, a kernel-mode driver on BSD, and on RouterOS in user space), the handshake (a fast 1-RTT key confirmation that proves both sides have the matching preshared key) lights up, and wg show shows the right latest handshake timestamp. Then you try to reach anything on the home side, and the packets vanish. No denied log, no ICMP unreachable, just silence. I have watched that silence cost a friend most of a Saturday, so this is the checklist I wish I had handed him before he started.

The setup that produces this is almost always the same shape. A MikroTik (RouterOS, the Linux-derived routing platform that runs on MikroTik’s RouterBOARD hardware) at the home, a cheap VPS (a virtual private server, basically a small Linux VM rented by the month) somewhere with a public IPv4, and a WireGuard tunnel between them so the VPS can act as a jump box into the home. From the VPS, you can ping the MikroTik’s tunnel address. You cannot ping the printer on 192.168.88.0/24, the NAS on 192.168.1.0/24, or whatever the actual LAN (local area network, the subnet your home devices live on) is called. None of that is wrong, but none of it is enough either, because the configuration only describes one half of a tunnel and does not mention what happens to traffic after it crosses.

Why the tunnel is up but the subnet is silent

WireGuard’s allowed-address field is not a route. It is an ACL (access control list) on what traffic the interface is allowed to decrypt and what source IPs the peer is trusted to send. When the VPS sends a packet to 192.168.88.10, the kernel looks up the route, finds the WireGuard interface, then checks the peer’s allowed-address list to confirm the destination is something that peer is supposed to be able to reach. If 192.168.88.0/24 is not in that list, the packet is dropped silently. A common mistake is to use 0.0.0.0/0 on one side and a real subnet on the other, which works for the side with 0.0.0.0/0 and silently breaks the other direction. The fix is mechanical: on each peer, list every subnet behind that peer, not just the peer’s own tunnel address.

The masquerade rule nobody remembers to write

WireGuard is a layer-3 (the network layer of the OSI model, where IP addresses live) tunnel. It carries IP packets. When a packet from the VPS reaches the MikroTik destined for 192.168.88.10, the MikroTik has to route it onto the LAN. If the LAN device sees the packet’s source as the VPS’s tunnel address (a 192.168.7.x address, probably), it will not know how to reply, because nothing on the LAN has a route to 192.168.7.0/24. Replies go to the default gateway, which is the MikroTik, which forwards them back into the tunnel, and the cycle repeats with no visible error.

Fixing it requires source NAT (network address translation, the rewrite your router does to a packet’s source address so replies can find their way back) so the LAN devices think the traffic came from the MikroTik. On RouterOS this is a single firewall rule under /ip firewall nat:

  • chain=srcnat with src-address=192.168.7.0/24 and dst-address=192.168.88.0/24
  • action=masquerade so the source is rewritten to the MikroTik’s LAN address
  • comment=wireguard-from-vps so future-you can find the rule six months from now

That is the line that makes the LAN device think the VPS is the router, which is what it needs to know to send replies back to a place that can route them.

RouterOS chains: input, forward, and which one drops your packet

RouterOS has separate firewall chains for traffic destined to the router itself (input), traffic passing through the router (forward), and traffic originating from the router (output). WireGuard traffic in from the tunnel is forward, not input, so a common config that locks down input to only the local network will not block tunneled traffic but also will not pass it. Default forward chain on a fresh MikroTik is accept, which is fine for testing but should be tightened in production. The symptom of a too-tight forward chain is identical to the masquerade problem, with the same silence, so the first thing to check when the tunnel is up but the subnet is not reachable is /ip firewall filter print where chain=forward. If you see a drop rule with no matching accept for the tunnel subnet, that is your culprit.

What the source material actually shows

The question that prompted this was posted on ServerFault, and the configuration snippet that came with it is the canonical “I followed a guide and half of it worked” file. The MikroTik side declares two interfaces: a wg-xxx-it with a real listen-port=36857 and a wireguard interface that is disabled=yes on listen-port=13231. The peer entry uses client-address=192.168.7.1/24, which on RouterOS 7 means the address the MikroTik hands out to the peer inside the tunnel, not the address the MikroTik itself uses inside the tunnel. The endpoint is wg.xxx-it.de:51820, which is the VPS’s hostname on its public WireGuard port. None of that is wrong, but none of it is enough either, because the configuration only describes one half of a tunnel and does not mention what happens to traffic after it crosses.

What I would tell past me

  • The allowed-address field is a per-peer ACL, not a route. List every subnet behind the peer, on both sides. A 0.0.0.0/0 on one side and a real subnet on the other will work for half the packets and silently drop the other half.
  • WireGuard does not handle NAT for you. If the LAN cannot route back to the tunnel subnet, you need a srcnat masquerade on the router doing the routing. Without it, the LAN replies to a place it has no route for, and the cycle never breaks.
  • RouterOS’s forward chain is the one that catches tunneled traffic, not input. A “secure” config that only locks down input will let the tunnel through but not pass it to the LAN. Check the chain you are actually filtering, not the one you assumed.
  • Log drops on the forward chain while debugging. RouterOS can log every dropped packet, which is the only way to tell the masquerade-missing case from the forward-chain-drops case from the allowed-address-mismatch case without external tooling. The throughput hit is real but temporary.

Trade-offs

Two approaches handle the same problem with different complexity. A full-tunnel default route on the VPS sends all VPS-originated traffic through the home uplink, which is simple to reason about and harder to misconfigure, but it inherits whatever bandwidth and asymmetry the home ISP provides (VPS has gigabit, home uplink has whatever the ISP gives you). A split-tunnel approach with only the home subnets routed through the tunnel is faster but requires careful allowed-address and NAT setup, and the cost of a mistake is exactly the silent failure described above. A third option is to run a proper routing protocol (BIRD, FRRouting) on the VPS and on the MikroTik and let it figure out the routes. That works but adds another daemon to maintain and another thing to break.

A separate trade-off is whether to use WireGuard at all. The same topology can be built with IPsec (the IP security protocol suite, older and more complex but supported natively by more hardware offload engines), with OpenVPN (a TLS-based VPN, slower but easier to debug because it speaks TCP and shows up in tcpdump as actual TLS), or with a Tailscale-style mesh (a coordination-server-driven overlay that handles the allowed-address and key rotation for you). For a single tunnel between a home router and a VPS, plain WireGuard is hard to beat on simplicity. For a topology that will grow to multiple sites, the mesh approach saves a lot of allowed-address upkeep.

A third trade-off is logging volume. RouterOS can log every dropped packet in the forward chain, but doing so at any volume slows the router noticeably. Default is to log nothing, which is why the symptom of “tunnel up, subnet down” is silence. Turn logging on for the duration of the debug, accept the throughput hit, then turn it off again. Anything else, and you are guessing which of the three failure modes you are in.

Leave a comment