[4.9.0] Policy-routing rule writes tunnel client virtual-IP as /8 — entire 10.0.0.0/8 routed into VPN tunnel; exclusion list cannot carve it out

[4.9.0] Policy-routing rule writes tunnel client virtual-IP as /8 — entire 10.0.0.0/8 routed into VPN tunnel; exclusion list cannot carve it out

Device: GL-MT6000 (Flint 2) Firmware: 4.9.0 VPN: WireGuard client (Surfshark, native integration login), VPN Dashboard in Policy Mode, two concurrent tunnels


Summary

On 4.9.0, the VPN Dashboard's policy routing installs an ip rule that expresses the tunnel's client virtual IP (a /32 host address) with an /8 prefix. This matches the entire 10.0.0.0/8 range, so all RFC1918 10-space traffic — including the router's own upstream WAN gateway/DNS — is force-routed into the tunnel. The VPN Exclude Specified Domain / IP List feature cannot fix this, because the capture happens via broad ip rule entries (including router-origin traffic) that the exclusion list does not act on.

Specifically, if the WAN port is configured with a DNS address in a 10.x.x.x range, only a single VPN tunnel can connect successfully.

Environment / topology

The router's WAN faces a private 10.9.9.0/24 network. The upstream gateway and DNS server is 10.9.9.1; the router's WAN IP is 10.9.9.2. The tunnel's assigned client virtual IP is 10.14.0.2.

Steps to reproduce

  1. GL-MT6000 on 4.9.0, WAN connected to a 10.x.x.x subnet whose gateway/DNS is a 10.x address (here 10.9.9.1).

  2. Configure a WireGuard VPN client (Surfshark native login), Policy Mode, and activate it. The client virtual IP is in 10.14.0.x.

  3. Observe the router can no longer reach any 10.x address on the WAN side (e.g. its DNS resolver at 10.9.9.1) — the traffic is routed into the tunnel and dies at the VPN exit.

  4. Try to fix via VPN Dashboard → tunnel → To → Exclude Specified Domain / IP List → add 10.9.9.1. It has no effect.

Evidence

ip rule — note rule 20000 (/8 on a host address) and rule 90109 (router-origin traffic to the tunnel table):

0:      from all lookup local
1:      from all iif lo lookup 16800
800:    from all lookup 9910 suppress_prefixlength 0
6000:   from all fwmark 0x1000/0xf000 lookup 1001
6000:   from all fwmark 0x2000/0xf000 lookup 1002
6000:   from all fwmark 0x8000/0xf000 lookup main
9000:   not from all fwmark 0/0xf000 lookup main
9910:   not from all fwmark 0/0xf000 blackhole
9920:   from all iif br-lan blackhole
10000:  from 10.14.0.2 lookup 1001
20000:  from all to 10.14.0.2/8 lookup 1001      <-- /8 on a /32 host address = matches all of 10.0.0.0/8
32766:  from all lookup main
32767:  from all lookup default
90109:  from all iif lo lookup 1001              <-- router-origin (locally generated) traffic sent to tunnel table

ip route show table 1001 — the tunnel table also carries a 10.0.0.0/8 route:

default dev wgclient1 proto static scope link
blackhole default proto static metric 254
10.0.0.0/8 dev wgclient1 proto static scope link

ip route get 10.9.9.1 and an unmarked lookup both resolve into the tunnel, not the WAN:

# ip route get 10.9.9.1
10.9.9.1 dev wgclient1 table 1001 src 10.14.0.2 uid 0

# ip route get 10.9.9.1 mark 0
10.9.9.1 dev wgclient1 table 1001 src 10.14.0.2 uid 0

nft list ruleset | grep -i mark returns nothing — confirming the capture is in ip rule policy routing, not nftables marks, which is why the fwmark-based client exclusion list has no bearing on it.

Expected behaviour

  • The tunnel client virtual IP should be associated with the tunnel as a /32 host (10.14.0.2/32), not /8. Rule 20000 should read to 10.14.0.2/32.

  • The tunnel table should not carry a blanket 10.0.0.0/8 route; a full-tunnel is served by the default route already present.

  • Directly-connected WAN subnets and the upstream gateway/DNS should remain reachable via the WAN and not be captured by the tunnel.

Actual behaviour

The entire 10.0.0.0/8 is routed into the tunnel. The router cannot reach its own upstream 10.x gateway/DNS, so its own DNS resolution fails whenever the tunnel is up; a second concurrent tunnel cannot resolve its endpoint hostname; and any 10.x resource the router needs is silently blackholed into the VPN exit. The exclusion list cannot correct it.

Likely root cause

The route/rule prefix for the tunnel client virtual IP appears to be derived using classful assumptions — a 10.x address treated as class A (/8) — rather than the /32 host address or the WireGuard interface's actual assigned prefix.

Impact

This breaks any deployment where the router's WAN faces a private 10.0.0.0/8 network — common in enterprise/MSP setups where the GL.iNet sits behind a firewall on a 10.x transit segment. It does not surface for typical home users (LAN 192.168.8.0/24, WAN 192.168.x or a public address), which is likely why it has gone unreported.

Workaround (not a fix)

Pointing the router's own DNS at a public resolver (reachable through the tunnel) restores resolution, and placing the WAN/transit link outside 10.0.0.0/8 avoids the capture — but the underlying /8 route remains and will still swallow any other 10.x destination the router must reach.

The routing output confirms that 10.9.9.1 is being selected by the generated 10.0.0.0/8 policy, although the cause of the /8 still needs verification. Please provide the redacted output of:

ip -4 addr show dev wgclient1

This will show whether the /8 originates from the WireGuard interface address or is introduced when firmware 4.9.0 generates the policy rules.

As a temporary, non-persistent workaround, a more-specific rule placed before priority 20000 should preserve access to the WAN transit subnet:

ip rule add priority 19999 to 10.9.9.0/24 lookup main
ip route get 10.9.9.1

The second command should then resolve through the WAN interface. Remove the test rule with ip rule del priority 19999; VPN restarts or reboots may also remove or reorder manual rules. This is only a workaround and does not correct the generated /8 route.

This reply was generated by AI. Please verify its accuracy.

GL.iNet Technical Support reviews and responds to relevant threads on business days. Please allow time for a staff response.

Output below — the /8 is on the interface address itself, so the routing rule and table are just downstream of it:

118: wgclient1: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1350 ...
    inet 10.14.0.2/8 brd 10.255.255.255 scope global wgclient1

The interface is flagged POINTOPOINT NOARP but carries a /8 address — a point-to-point tunnel endpoint should be a /32 host address, so the /8 is wrong for the interface type and is what pulls all of 10.0.0.0/8 into the tunnel table.

The WireGuard client interface is being brought up with a /8 prefix on what should be a /32 point-to-point host address, so the router installs a connected route for all of 10.0.0.0/8 into the tunnel and a matching policy rule (the earlier to 10.14.0.2/8 lookup 1001).

I can't see the raw provisioned config through the native Surfshark integration, so I can't tell whether Surfshark supplies Address = 10.14.0.2/8 or whether the 4.9.0 importer defaults the prefix to /8 — but either way the client tunnel address should be sanitised to /32. Could you confirm which, and whether the importer can enforce /32 on client interfaces?

For context: my own deployment is already resolved (public resolver + transit link off 10-space). I'm following up to get the underlying /8 corrected in firmware so other 10-space deployments don't hit it.

The output confirms that the /8 originates at the address assigned to wgclient1; the generated connected route and policy rule are consequences of that prefix. It does not, by itself, show whether Surfshark’s provisioning response supplied /8 or firmware 4.9.0 added it.

Also, POINTOPOINT does not technically require a /32 address on a WireGuard interface, so universally rewriting provider prefixes to /32 could alter configurations that intentionally use a subnet. The firmware issue to verify is whether the native Surfshark integration incorrectly generates /8, or whether VPN policy routing should avoid treating that interface prefix as a destination route when it overlaps the WAN. Confirmation of the provisioning source and any firmware change will require GL.iNet staff review; please do not post the generated profile or API response publicly because it may contain the WireGuard private key or account data.

This reply was generated by AI. Please verify its accuracy.

GL.iNet Technical Support reviews and responds to relevant threads on business days. Please allow time for a staff response.

Understood, thanks i will await the staff review. Let me know if i can assist further.