Intermittent WireGuard tunnel stops handshaking until local listen port is changed
-
Hi,
I am investigating an intermittent WireGuard issue on pfSense CE 2.9.0.
Environment:
- pfSense CE 2.9.0-RELEASE
- FreeBSD 16.0-CURRENT
- pfSense-pkg-WireGuard 0.2.13_4
- Native FreeBSD if_wg
- Multiple independent WireGuard tunnels running at the same time
- Five tunnels connect to different Proton VPN endpoints
- One additional WireGuard tunnel connects to a server under my own control
The issue:
An individual Proton WireGuard tunnel works normally for many hours and then suddenly stops completing handshakes.
The interface itself remains:
UP
RUNNING
LOWER_UPThe route to the peer is still correct.
The UDP socket is still bound.
A PF state exists.
PersistentKeepalive is set to 25 seconds.The affected tunnel continues trying to initiate new WireGuard handshakes, but no handshake response is received.
Changing only the local WireGuard listen/source port immediately restores the connection.
For example:
Tunnel A:
local port 51998 -> failure
changed to 52998 -> immediately working againTunnel B:
local port 51825 -> failure
changed to 52825 -> working againTunnel C:
local port 51895 -> failure
changed to 52895 -> working againThese are different WireGuard peers and different Proton VPN endpoints, so the problem does not appear to be limited to one remote server.
The most recent occurrence was captured with if_wg debugging enabled.
The affected tunnel had not completed a handshake for about six minutes.
The kernel repeatedly logged:
Sending handshake initiation
Handshake did not complete after 5 seconds, retryingThis continued through 20 retries and then started another retry sequence.
A tcpdump directly on the physical WAN interface showed that the WireGuard handshake initiation packets were actually leaving the firewall.
Example:
local-WAN:51895 -> remote-peer:51820 UDP length 148
local-WAN:51895 -> remote-peer:51820 UDP length 148
local-WAN:51895 -> remote-peer:51820 UDP length 148
local-WAN:51895 -> remote-peer:51820 UDP length 148They were sent approximately every five seconds.
No corresponding 92-byte WireGuard handshake responses were received.
tcpdump reported:
0 packets dropped by kernel
At exactly the same time, other WireGuard interfaces on the same pfSense system continued completing handshakes successfully.
I also have an independent WireGuard tunnel to a Linux server under my own control.
During the failure of the Proton tunnel, this control tunnel continued completing normal WireGuard handshakes through the same WAN interface.
For example, immediately before the watchdog detected the failed tunnel:
control server -> pfSense: UDP length 148
pfSense -> control server: UDP length 92The control tunnel successfully repeated this handshake approximately every two minutes before, during and after the failure.
Other Proton WireGuard tunnels also continued completing handshakes while the affected tunnel was stuck.
This makes a general WAN outage or a complete if_wg failure unlikely.
The affected tunnel recovered within a few seconds after changing only its local listen port.
What I am trying to determine is whether there could be state inside if_wg or the pfSense WireGuard package which is reset when the local listen port is changed.
In particular:
-
Is there any known issue in FreeBSD 16 if_wg where an individual peer can get stuck in this state?
-
Does changing the WireGuard listen port reset more than just the UDP socket / 5-tuple?
-
Could if_wg theoretically continue generating correctly-sized 148-byte handshake initiation packets while the cryptographic/session state of those packets is invalid?
-
Are there additional if_wg counters, debug options or kernel tracing methods that would help verify the validity of the transmitted handshake initiation?
-
Has anyone seen similar behaviour where changing only the local WireGuard source port immediately restores an otherwise unchanged peer?
I have diagnostic snapshots from several independent occurrences, including:
- wg show
- ifconfig
- routing table
- PF state
- UDP socket state
- if_wg debug output
- WAN tcpdump
- simultaneous packet capture from the independent control WireGuard server
I can provide sanitized logs or packet captures if useful.
At the moment I am not assuming that this is necessarily a pfSense/if_wg bug. The remaining possibilities also include state somewhere in the upstream network path or on the VPN provider side. I am mainly trying to determine whether there is anything inside if_wg that could explain why changing only the local listen port immediately restores the tunnel.
Thanks!
Privacy Policy · Cookie Policy