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:
- In OpenWRT LuCI ⇒ Network ⇒ Interfaces, select Add New Interface
- Enter an interface name and select Wireguard VPN as the protocol
- Click “Load Configuration”
- Paste or upload the configuration file
- 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
- Official documentation: https://docs.openwrt.melmac.ca/pbr/
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)
EOFCheck 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
- https://forum.openwrt.org/t/log-spam-reloading-pbr-wan6-interface-routing-due-to-ifupdate-of-wan6-eth0-2/178165
- https://github.com/openwrt/openwrt/issues/22122
- https://openwrt.org/docs/guide-user/network/protocol.dhcp#dhcp_client_scripts1
- https://openwrt.org/docs/guide-user/network/ipv6/configuration?s%5B%5D=options#protocol_dhcpv6
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:
- In OpenWRT LuCI ⇒ Services ⇒ Policy Routing ⇒ Basic Configuration, ensure IPv6 Support is enabled.
- 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)
- In OpenWRT LuCI ⇒ Network ⇒ Firewall ⇒ Zone settings ⇒ your VPN zone, select Edit, then in Advanced Settings ensure IPv6 Masquerading is checked.
- 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>)
- 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.