# OpenWrt: Fix WireGuard Connectivity with MWAN3 by Excluding the VPN Endpoint

September 28, 2025 5-minute read

### Overview Link to heading

When using WireGuard together with MWAN3 on OpenWrt, the tunnel can fail to establish or flap when the peer’s IP is routed into the tunnel itself. This is a classic routing bootstrap problem: WireGuard wants to route 0.0.0.0/0 into the tunnel, but the UDP packets to the peer’s public endpoint also get captured, so they never reach the Internet to bring the tunnel up.

This article explains the symptoms, root cause, and a minimal, robust fix: add an MWAN3 rule that forces traffic to the WireGuard peer endpoint IP to go out via a physical WAN policy (not the tunnel). Optionally, assign a metric to the WireGuard interface to keep tables predictable.

### Environment Link to heading

- OpenWrt 24.10.x
- MWAN3 for multi-WAN policy routing
- WireGuard interface configured with broad `allowed_ips` covering default route (`0.0.0.0/1` and `128.0.0.0/1` or `0.0.0.0/0`)

### Symptoms Link to heading

- `wg show` indicates the interface is listening, but `transfer: 0 B received` persists after bringing the tunnel up.
- Intermittent reachability to public IPs until routing settles.
- `ip route` shows multiple defaults via WANs; a host route to the peer IP may exist but is still overridden by policy routing once MWAN3 applies rules.

Example observations (sanitized):

    wg show
    interface: wireguard
      public key: <redacted>
      listening port: 39345
    peer: <peer-public-key-redacted>
      endpoint: 203.0.113.55:51821
      allowed ips: 0.0.0.0/1, 128.0.0.0/1
      transfer: 0 B received, 5.6 KiB sent

    ip route
    default via 192.0.2.1 dev wan0 proto static src 192.0.2.10
    default via 198.51.100.1 dev wan1 proto static src 198.51.100.10 metric 20
    203.0.113.55 via 198.51.100.1 dev wan1 proto static metric 20

### Root Cause Link to heading

With default-route `allowed_ips`, WireGuard installs routes so that all outbound traffic prefers the tunnel. MWAN3 then applies policy rules that also match “all traffic,” including the UDP packets to the WireGuard peer’s public IP. If those packets are selected to go via the `wireguard` interface (or a table whose default is the tunnel), the handshake cannot succeed. This creates a chicken-and-egg dependency.

### Fix: Exclude the WireGuard Endpoint from MWAN3 Default Policy Link to heading

Force traffic to the WireGuard peer public endpoint to use a physical WAN policy. This guarantees the handshake packets always reach the Internet outside of the tunnel.

Steps:

1. Resolve the peer endpoint IP (if you only have a hostname)

    nslookup vpn.example.com
    # => use the returned A/AAAA address(es) in the rule below

2. Add an MWAN3 rule targeting the endpoint IP

Edit `/etc/config/mwan3` and place this rule before the default v4 rule so it takes precedence:

    config rule 'wireguard_endpoint'
        option dest_ip '203.0.113.55'   # peer public IP
        option proto 'udp'
        option use_policy 'wan_only'     # a policy that prefers a physical WAN
        option family 'ipv4'

Notes:

- Use the actual public IP of your WireGuard server. MWAN3 rules match IPs, not hostnames.
- If you have multiple WAN policies (e.g., `wan_only`, `wphone_only`), choose the one that must carry the VPN handshake.

3. (Optional) Assign a metric on the WireGuard interface

This is not strictly required for the fix but keeps routing behavior deterministic when multiple defaults exist.

Edit `/etc/config/network`:

    config interface 'wireguard'
        option proto 'wireguard'
        option private_key '<redacted>'
        list addresses '192.168.3.2/32'
        option metric '5'

4. Apply changes

    /etc/init.d/network restart && /etc/init.d/mwan3 restart

### Validation Link to heading

Confirm that the endpoint is routed via a physical WAN and that the tunnel is passing traffic.

    # Verify policy routing for the endpoint
    ip route get 203.0.113.55

    # MWAN3 status should show your WANs online
    mwan3 status | sed -n '1,120p'

    # WireGuard should show RX increasing after a few seconds
    wg show

Expected results:

- `ip route get <peer-ip>` resolves to a physical WAN device/policy, not the `wireguard` interface.
- `wg show` shows non-zero bytes received and a recent handshake time.

### Operational Considerations Link to heading

- Endpoint IP changes: If the server endpoint is behind DDNS, you must update the rule when its IP changes. Options include:
  - Use a small script triggered by DDNS updates to modify the MWAN3 rule and reload.
  - Maintain an IP set and populate it from DDNS; match the set in firewall/PBR and keep MWAN3 in sync.
- IPv6: Repeat the approach with an IPv6 rule if your peer uses IPv6. Ensure `family 'ipv6'` and the correct policy are set.
- Multiple peers: Create one rule per peer endpoint IP.
- Ordering: Keep the endpoint rule above broad default rules so it always wins.

### Minimal Example Config Snippets (Sanitized) Link to heading

`/etc/config/network` (relevant parts):

    config interface 'wireguard'
        option proto 'wireguard'
        option private_key '<redacted>'
        list addresses '192.168.3.2/32'
        option metric '5'

    config wireguard_wireguard
        option description 'peer-1'
        option public_key '<peer-public-key-redacted>'
        option endpoint_host 'vpn.example.com'
        option endpoint_port '51821'
        list allowed_ips '0.0.0.0/1'
        list allowed_ips '128.0.0.0/1'
        option route_allowed_ips '1'

`/etc/config/mwan3` (relevant parts):

    config policy 'wan_only'
        list use_member 'wan_m1_w3'
        option last_resort 'unreachable'

    config rule 'wireguard_endpoint'
        option dest_ip '203.0.113.55'
        option proto 'udp'
        option use_policy 'wan_only'
        option family 'ipv4'

    config rule 'default_rule_v4'
        option dest_ip '0.0.0.0/0'
        option use_policy 'wan_only'
        option family 'ipv4'
        option proto 'all'
        option sticky '0'

### Why This Works Link to heading

The explicit MWAN3 rule ensures that traffic to the peer’s public IP bypasses any routes that prefer the tunnel. This breaks the bootstrap loop and guarantees handshake packets traverse a real WAN uplink. Once the tunnel is established, the broad `allowed_ips` continue to route general traffic through WireGuard as intended.

### References Link to heading

- Session log and configs (internal): `~/Downloads/chat-MWAN3 WireGuard Routing Fix 🌐.txt`
- OpenWrt MWAN3 documentation: `https://openwrt.org/docs/guide-user/network/wan/multiwan/mwan3`
- WireGuard documentation: `https://www.wireguard.com/`
- OpenWrt WireGuard (UG): `https://openwrt.org/docs/guide-user/services/vpn/wireguard`