mirror of
https://github.com/safing/portmaster.git
synced 2026-05-20 20:40:36 +00:00
interop/ivpn: suppress network-derived location while VPN is active
When IVPN connects, virtual interface IPs can cause netenv to detect the VPN egress as the device's physical location, leading to a wrong SPN home hub selection. Fix this by gating interface- and traceroute- based location methods behind a new flag. - Add DisableNetworkDerivedLocation(bool) to netenv/location.go backed by an atomic.Bool; getLocationFromInterfaces and getLocationFromTraceroute return early when the flag is set - Call DisableNetworkDerivedLocation(true) in onConnectionStarting and DisableNetworkDerivedLocation(false) in onConnectionStopped and in the connectIvpnClient defer (covers unexpected client disconnects) https://github.com/safing/portmaster-shadow/issues/34
This commit is contained in:
@@ -7,11 +7,17 @@ import (
|
||||
|
||||
"github.com/ivpn/desktop-app/daemon/protocol/ivpnclient"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
)
|
||||
|
||||
// notification handler: VPN connection is going to start
|
||||
func (i *InteropIvpn) onConnectionStarting(wc *mgr.WorkerCtx, _ string, messageData string) {
|
||||
// While VPN is active, ignore network-derived location sources.
|
||||
// Virtual VPN interfaces/IPs can skew detected device location
|
||||
// and lead to selecting an incorrect SPN home hub.
|
||||
netenv.DisableNetworkDerivedLocation(true)
|
||||
|
||||
connInfo := ivpnclient.ConnectionStarting{}
|
||||
err := json.Unmarshal([]byte(messageData), &connInfo)
|
||||
if err != nil {
|
||||
@@ -38,6 +44,9 @@ func (i *InteropIvpn) onConnectionStarting(wc *mgr.WorkerCtx, _ string, messageD
|
||||
|
||||
// notification handler: VPN connection stopped
|
||||
func (i *InteropIvpn) onConnectionStopped(wc *mgr.WorkerCtx, _ string, _ string) {
|
||||
// Re-enable network-derived location methods now that VPN is inactive.
|
||||
netenv.DisableNetworkDerivedLocation(false)
|
||||
|
||||
status := *i.getStatus()
|
||||
status.vpnConnection = vpnConnectionInfo{}
|
||||
status.connectedInfo = nil
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/safing/portmaster/base/notifications"
|
||||
"github.com/safing/portmaster/service/firewall/interception"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
@@ -115,6 +116,9 @@ var notifWarnOldVersion atomic.Pointer[notifications.Notification]
|
||||
// Synchronously connects to the IVPN client, sets up message handlers
|
||||
func (i *InteropIvpn) connectIvpnClient(wc *mgr.WorkerCtx) error {
|
||||
defer func() {
|
||||
// Re-enable network-derived location methods when disconnecting from IVPN client, since VPN is no longer active.
|
||||
netenv.DisableNetworkDerivedLocation(false)
|
||||
|
||||
// Clear client status on disconnect
|
||||
i.setStatus(nil)
|
||||
// Reset DNS tracking state
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
@@ -33,6 +34,12 @@ var (
|
||||
locationsLock sync.Mutex
|
||||
gettingLocationsLock sync.Mutex
|
||||
locationNetworkChangedFlag = GetNetworkChangedFlag()
|
||||
|
||||
// disableNetworkDerivedLocation disables location methods that depend on network
|
||||
// configuration, such as reading interface IPs or using traceroute.
|
||||
// Use this when a VPN is known to be active and network-derived location would
|
||||
// reflect VPN egress rather than the device's physical uplink.
|
||||
disableNetworkDerivedLocation atomic.Bool
|
||||
)
|
||||
|
||||
func prepLocation() (err error) {
|
||||
@@ -40,6 +47,13 @@ func prepLocation() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// DisableNetworkDerivedLocation disables or enables location methods that depend on network
|
||||
// configuration, such as reading interface IPs or using traceroute. Use this when a VPN is known to be active and
|
||||
// network-derived location would reflect VPN egress rather than the device's physical uplink.
|
||||
func DisableNetworkDerivedLocation(disable bool) {
|
||||
disableNetworkDerivedLocation.Store(disable)
|
||||
}
|
||||
|
||||
// DeviceLocations holds multiple device locations.
|
||||
type DeviceLocations struct {
|
||||
All []*DeviceLocation
|
||||
@@ -332,6 +346,10 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) {
|
||||
}
|
||||
|
||||
func getLocationFromInterfaces(dls *DeviceLocations) (v4ok, v6ok bool) {
|
||||
if disableNetworkDerivedLocation.Load() {
|
||||
return false, false
|
||||
}
|
||||
|
||||
globalIPv4, globalIPv6, err := GetAssignedGlobalAddresses()
|
||||
if err != nil {
|
||||
log.Warningf("netenv: location: failed to get assigned global addresses: %s", err)
|
||||
@@ -362,6 +380,10 @@ func getLocationFromUPnP() (ok bool) {
|
||||
*/
|
||||
|
||||
func getLocationFromTraceroute(dls *DeviceLocations) (dl *DeviceLocation, err error) {
|
||||
if disableNetworkDerivedLocation.Load() {
|
||||
return nil, fmt.Errorf("skipped network-derived location")
|
||||
}
|
||||
|
||||
// Create connection.
|
||||
conn, err := icmp.ListenPacket("ip4:icmp", "")
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user