Far Gateway host route not created for WireGuard-assigned gateway (monitor traffic silently falls back to WAN)
support
**Important notices**
Before you add a new report, we ask you kindly to acknowledge the following:
- [x] I have read the contributing guidelines at https://github.com/opnsense/core/blob/master/CONTRIBUTING.md
- [x] I am convinced that my issue is new after having checked both open and closed issues at https://github.com/opnsense/core/issues?q=is%3Aissue
- [x] AI tools were used to create at least part of the text submitted herewith.
If AI was used, please disclose:
- Model used: Claude
- Extent of AI involvement: Drafting post with input
## Describe the bug
When a WireGuard client interface is assigned as a gateway with **Far Gateway** checked and an arbitrary (intentionally unassigned) placeholder IP address, OPNsense does not create the static host route needed to actually route monitor traffic through the tunnel — despite the config being valid and `dpinger_status()` correctly reporting the configured gateway IP. The result is that gateway monitoring traffic silently falls through to the default WAN route instead of traversing the VPN tunnel, and the dpinger-reported status (loss/latency) reflects the WAN path, not the tunnel. This makes gateway monitoring on a WireGuard-based gateway meaningless — the tunnel can be completely down while monitoring reports it healthy.
## Environment
- OPNsense 26.7.1_1-amd64
- FreeBSD 15.1-RELEASE-p1
- Native WireGuard (VPN > WireGuard), client instance assigned as an interface
- Gateway configured with **Far Gateway** enabled, arbitrary placeholder IP address, and a Monitor IP
## Setup
1. WireGuard client instance assigned as an interface (e.g. `wg2`), tunnel address `10.x.x.x/32`.
2. Gateway created for this interface:
- Far Gateway: checked
- IP Address (gateway field): an arbitrary unused placeholder, e.g. `10.x.x.1` (not a real, routable address — this is the documented/expected pattern for WireGuard gateways in OPNsense, since a WG tunnel has no real on-link next-hop)
- Monitor IP: a real internet host (e.g. a public DNS resolver)
## Expected behavior
OPNsense creates a static host route anchoring the placeholder gateway IP to the WireGuard interface (this is the whole purpose of the Far Gateway flag), and a derived host route for the Monitor IP via that gateway — so dpinger's probe traffic actually traverses the tunnel.
## Actual behavior
No such route is created, at any point — not after initial gateway creation, not after Save + Apply on the gateway config, and not after a full "Reload all services." `route get <monitor IP>` continues to resolve via the WAN default route indefinitely.
## Reproduction / evidence
**Config confirmed correct** — `dpinger_status()` shows the intended gateway IP:
```
[WAN_ProtonVPN] => Array
(
[status] => none
[monitor] => 8.8.8.8
[gateway] => 10.x.x.1
...
[loss] => 0.0 %
)
```
**But the kernel route table disagrees** — no route exists for the gateway IP or monitor IP via the tunnel interface, at all:
```
# netstat -rn -f inet | grep "10.x.x.1"
(no output)
# route get 8.8.8.8
route to: dns.google
destination: default
gateway: <WAN gateway>
interface: em1
flags: <UP,GATEWAY,DONE,STATIC>
```
**Confirmed via packet capture that monitor traffic is actually leaking out WAN, not the tunnel:**
```
# tcpdump -ni wg2 icmp
listening on wg2, link-type NULL (BSD loopback)
0 packets captured <-- nothing, despite ping succeeding
# tcpdump -ni em1 icmp and host 8.8.8.8
09:38:55 IP <WAN IP> > 8.8.8.8: ICMP echo request
09:38:55 IP 8.8.8.8 > <WAN IP>: ICMP echo reply
```
The source address of the ICMP echo (the tunnel's `-S 10.x.x.2`) was rewritten by outbound NAT on the way out `em1` — proof the packet never touched the tunnel despite dpinger and manual pings both reporting clean, low-latency success.
**System log during a full service reload shows the likely proximate cause** — the route-resolution logic explicitly fails to match the placeholder gateway IP against any known interface subnet (expected, since it's intentionally unassigned) and falls back to a default bind instead of applying the Far Gateway pinning:
```
/usr/local/sbin/pluginctl: Chose to bind WAN_ProtonVPN on 10.x.x.2 since we could not find a proper match.
```
## Workaround
Manually add the missing route as a **static route** (System > Routes > Configuration), not relying on the dynamic Far-Gateway route builder:
- Network: `<monitor IP>/32`
- Gateway: the WireGuard gateway object
This is a declarative config entry (persists across reboot, unlike a manual `route add` at the shell) and correctly anchors monitor traffic to the tunnel. Confirmed via repeat of the above tcpdump test that ICMP now correctly traverses `wg2` after adding this route, and that `dpinger_status()` correctly reports `down`/100% loss on a real tunnel outage once this is in place (previously it never did, regardless of actual tunnel state).
## Impact
Anyone using a WireGuard client gateway with Far Gateway + Monitor IP for health checking, multi-WAN/VPN failover via Gateway Groups, or any alerting built on gateway status, gets **silently incorrect monitoring** — the gateway will report healthy even when the tunnel is completely down, because the probe traffic never actually uses the tunnel. This directly undermines gateway-group-based VPN failover, since the failover trigger never fires.
1 条评论