Using a VPN is great for privacy reasons. Even better than that is not having to configure the client on every device you own — so instead, set up your router to send all traffic through the VPN.

Client vs. server

This section is notes on how to set up your router as a VPN client, not as a VPN server! Moreover, it is intended for routing all of your traffic through a VPN, not a split-tunnel routing approach. For that, see Site-to-site VPN.

I’m using MullvadVPN as a provider (good business model IMO) for this example. To start with, you’ll need a Wireguard client certificate from the provider and the necessary software installed on your router.

Getting a Mullvad Certificate

Once you’ve signed up for Mullvad, you can configure the network VPN by downloading a Wireguard certificate from the Mullvad account page. Alternatively, see Mullvad and DNS Hijacking if you want to run your own DNS server.

Once you have this certificate:

  1. In OpenWRT LuCI Network Interfaces, select Add New Interface
  2. Enter an interface name and select Wireguard VPN as the protocol
  3. Click “Load Configuration”
  4. Paste or upload the configuration file
  5. Save & apply the interface changes

Policy-Based Routing

When you have multiple VPN tunnels, things get really tricky to route.

On OpenWRT, the policy-based routing (PBR) package pbr and associated luci-app-pbr frontend package provide a capability to do this. Once installed, you’ll find a Services Policy Routing section in the LuCI web interface.

See also

The PBR page will indicate what it thinks are available service gateways — interfaces which can be used to route outbound traffic through — and which one of them is selected as the default.

Applying changes

I’ve noticed that almost all changes I expect to immediately propagate to PBR services don’t. I recommend rebooting the entire router when you make a change you’d expect to change the routing table — even restarting the PBR service doesn’t seem to guarantee it works right.

This is especially true for changing the default service gateway!

Intermittent packet loss

If you experience packet loss when using PBR, it’s likely because the routing table is being periodically rebuilt. Check the system log for sporadic outputs of PBR restarting and rebuilding the routing table.

IPv6 RA updates

I experienced this problem because of IPv6 RA updates.

Check for the on_interface_reload wan6 message. Enable odhcpcd v6 syslogging for debugging:

cat << "EOF" > /etc/odhcp6c.user.d/00-logger
logger -t ${0##*/} ${@} $(env)
EOF

Check the syslog looking for ra-update messages. If there’s loads of them, this may be the cause. Try upping the ra_holdoff; default is 3, I upped it to 60. Why not? Idk what effect that has, but it seems like it might’ve helped. This option doesn’t seem to be exposed in LuCI, so set it via the config file /etc/config/network:

config interface 'wan6'
    option device 'eth1'
    option proto 'dhcpv6'  # <-- find the interface with this protocol
    option reqaddress 'try'
    option reqprefix 'auto'
    option peerdns '0'
    option ra_holdoff '60'  # < -- and add this line

An old forum post mentions an option added to PBR to ignore dhcpv6-triggered interface reload events. I cannot find this option to save my life, but did find proc_reload_delay. This option is not exposed in LuCI; edit /etc/config/pbr:

config pbr 'config'
    ...
    proc_reload_delay '10'
    ...

I’m not sure if 10 is a good value or not (nor what the default is), but it seemed to help slightly.

See also

Strict enforcement

The “Strict enforcement” option does the following:

  • Block traffic forwarding to a given destination if the desired interface is down (i.e. if your VPN interface goes down, traffic will not flow over the WAN instead — it just won’t flow)
  • Disables forwarding during routing table reloads

For some reason, with strict enforcement enabled, one of my site-to-site VPNs would not connect, but the others would. It failed at the Wireguard handshake phase; it seemed like my router never even sent the handshake initiation packets. Disabling strict enforcement fixed this. TBD if I start to see WAN leaks.

That wasn’t it. This switch seems to have made no difference and it was just a fluke that it started working.

See also

Setting a Wireguard VPN as default

The following settings must be configured for a Wireguard VPN to be selected as the default interface:

  • VPN interface General Settings IP addresses must be set, both IPv4 and IPv6
  • VPN interface General Settings No Host Routes must be unchecked
  • VPN interface Advanced Settings Force Link is checked
  • VPN interface Advanced Settings Use defautl gateway is checked
  • VPN interface Advanced Settings Use gateway metric is unchecked
  • VPN interface Peers may have only one enabled peer at a time
    • You can have multiple peers preconfigured for quickly switching VPN servers, but more than one at a time seems to break everything
    • Peer Disabled must be unchecked
    • Peer Allowed IPs must list the following:
      • 0.0.0.0/0
      • ::0/0
    • Peer Route Allowed IPs must be checked
    • Peer Endpoint Host must be filled in
    • Peer Endpoint Port must be filled in
  • VPN interface Firewall Settings Zone must be set to a firewall zone with the following settings in Network Firewall <zone>:
    • Input: reject
    • Output: accept
    • Intra-zone forward: reject
    • Masquerading: checked
    • Allow forward to destination zones: empty
    • Allow forward from source zones: list all the zones you want to send traffic over the VPN. Typically, this is your LAN zone.

Routing does not apply on startup

See also

Routing IPv6

For some reason, IPv6 traffic routing did not work out-of-the-box and after a few weeks I realized I was leaking v6 traffic (i.e. IPv4 was flowing out over the VPN as expected, but v6 was still using my ISP routes).

A combination of comments on this GitHub issue:

See also

and some custom settings fixed it. What I had to do was:

  1. In OpenWRT LuCI Services Policy Routing Basic Configuration, ensure IPv6 Support is enabled.
  2. In OpenWRT LuCI Services Policy Routing Policies, add a new policy with the following settings:
    • Enabled: checked
    • Name: “IPv6 over VPN” (or whatever you want)
    • Remote addresses / domains: ::/0
    • Chain: prerouting
    • Interface: MullvadVPN (whatever your VPN interface is)
  3. In OpenWRT LuCI Network Firewall Zone settings your VPN zone, select Edit, then in Advanced Settings ensure IPv6 Masquerading is checked.
  4. In OpenWRT LuCI Network Routing Static IPv6 Routes, add a new route with the following settings:
    • Interface: MullvadVPN (whatever your VPN interface is)
    • Target: ::/0
    • Metric: 256
    • Table: pbr_MullvadVPN (pbr_<your VPN interface>)

Save & apply changes.

Why?

I don’t think all of this should be necessary just to get IPv6 traffic going over the VPN. The docs for OpenWRT’s PBR package clearly state “IPv6 is fully supported”… not sure why I had to do all this, and there may be a better way.

To verify this is working properly, go to your favorite what’s-my-IP site and make sure that both IPv4 and IPv6 addresses appear from the same geolocation, and that neither is your actual home. I personally like https://whatismyip.com ; it clearly shows independent data for both v4 and v6 addresses.