diff --git a/daemon/service/service_connect.go b/daemon/service/service_connect.go index d719e4e4..ca8c0393 100644 --- a/daemon/service/service_connect.go +++ b/daemon/service/service_connect.go @@ -232,7 +232,7 @@ func (s *Service) Connect(params types.ConnectionParams) (err error) { params.OpenVpnParameters.Obfs4proxy = obfsproxy.Config{} } - return s.connectOpenVPN(originalEntryServerInfo, connectionParams, params.ManualDNS, params.Metadata.AntiTracker, params.FirewallOn, params.FirewallOnDuringConnection, params.OpenVpnParameters.Obfs4proxy) + return s.connectOpenVPN(originalEntryServerInfo, connectionParams, params.ManualDNS, params.Metadata.AntiTracker, params.FirewallOn, params.FirewallOnDuringConnection, params.OpenVpnParameters.Obfs4proxy, v2RayWrapper) } else if vpn.Type(params.VpnType) == vpn.WireGuard { if len(params.WireGuardParameters.EntryVpnServer.Hosts) < 1 { @@ -287,14 +287,14 @@ func (s *Service) Connect(params types.ConnectionParams) (err error) { params.WireGuardParameters.Mtu) } - return s.connectWireGuard(originalEntryServerInfo, connectionParams, params.ManualDNS, params.Metadata.AntiTracker, params.FirewallOn, params.FirewallOnDuringConnection) + return s.connectWireGuard(originalEntryServerInfo, connectionParams, params.ManualDNS, params.Metadata.AntiTracker, params.FirewallOn, params.FirewallOnDuringConnection, v2RayWrapper) } return fmt.Errorf("unexpected VPN type to connect (%v)", params.VpnType) } // connectOpenVPN start OpenVPN connection -func (s *Service) connectOpenVPN(originalEntryServerInfo *svrConnInfo, connectionParams openvpn.ConnectionParams, manualDNS dns.DnsSettings, antiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool, obfsproxyConfig obfsproxy.Config) error { +func (s *Service) connectOpenVPN(originalEntryServerInfo *svrConnInfo, connectionParams openvpn.ConnectionParams, manualDNS dns.DnsSettings, antiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool, obfsproxyConfig obfsproxy.Config, v2rayWrapper *v2r.V2RayWrapper) error { createVpnObjfunc := func() (vpn.Process, error) { prefs := s.Preferences() @@ -420,11 +420,11 @@ func (s *Service) connectOpenVPN(originalEntryServerInfo *svrConnInfo, connectio return vpnObj, nil } - return s.keepConnection(originalEntryServerInfo, createVpnObjfunc, manualDNS, antiTracker, firewallOn, firewallDuringConnection) + return s.keepConnection(originalEntryServerInfo, createVpnObjfunc, manualDNS, antiTracker, firewallOn, firewallDuringConnection, v2rayWrapper) } // connectWireGuard start WireGuard connection -func (s *Service) connectWireGuard(originalEntryServerInfo *svrConnInfo, connectionParams wireguard.ConnectionParams, manualDNS dns.DnsSettings, antiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool) error { +func (s *Service) connectWireGuard(originalEntryServerInfo *svrConnInfo, connectionParams wireguard.ConnectionParams, manualDNS dns.DnsSettings, antiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool, v2rayWrapper *v2r.V2RayWrapper) error { // stop active connection (if exists) if err := s.Disconnect(); err != nil { return fmt.Errorf("failed to connect. Unable to stop active connection: %w", err) @@ -477,10 +477,10 @@ func (s *Service) connectWireGuard(originalEntryServerInfo *svrConnInfo, connect return vpnObj, nil } - return s.keepConnection(originalEntryServerInfo, createVpnObjfunc, manualDNS, antiTracker, firewallOn, firewallDuringConnection) + return s.keepConnection(originalEntryServerInfo, createVpnObjfunc, manualDNS, antiTracker, firewallOn, firewallDuringConnection, v2rayWrapper) } -func (s *Service) keepConnection(originalEntryServerInfo *svrConnInfo, createVpnObj func() (vpn.Process, error), initialManualDNS dns.DnsSettings, initialAntiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool) (retError error) { +func (s *Service) keepConnection(originalEntryServerInfo *svrConnInfo, createVpnObj func() (vpn.Process, error), initialManualDNS dns.DnsSettings, initialAntiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool, v2rayWrapper *v2r.V2RayWrapper) (retError error) { prefs := s.Preferences() if !prefs.Session.IsLoggedIn() { return srverrors.ErrorNotLoggedIn{} @@ -525,7 +525,7 @@ func (s *Service) keepConnection(originalEntryServerInfo *svrConnInfo, createVpn } // start connection - connErr := s.connect(originalEntryServerInfo, vpnObj, dns, antitracker, firewallOn, firewallDuringConnection) + connErr := s.connect(originalEntryServerInfo, vpnObj, dns, antitracker, firewallOn, firewallDuringConnection, v2rayWrapper) if connErr != nil { log.Error(fmt.Sprintf("Connection error: %s", connErr)) if s._requiredVpnState == Connect { @@ -583,7 +583,7 @@ func (s *Service) keepConnection(originalEntryServerInfo *svrConnInfo, createVpn // We need this info to notify correct data about vpn.CONNECTED state: for V2Ray connection the original parameters are overwriten by local V2Ray proxy params ('127.0.0.1:local_port') // - Param 'firewallOn' - enable firewall before connection (if true - the parameter 'firewallDuringConnection' will be ignored). // - Param 'firewallDuringConnection' - enable firewall before connection and disable after disconnection (has effect only if Firewall not enabled before) -func (s *Service) connect(originalEntryServerInfo *svrConnInfo, vpnProc vpn.Process, manualDNS dns.DnsSettings, antiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool) error { +func (s *Service) connect(originalEntryServerInfo *svrConnInfo, vpnProc vpn.Process, manualDNS dns.DnsSettings, antiTracker types.AntiTrackerMetadata, firewallOn bool, firewallDuringConnection bool, v2rayWrapper *v2r.V2RayWrapper) error { var connectRoutinesWaiter sync.WaitGroup // stop active connection (if exists) @@ -812,6 +812,12 @@ func (s *Service) connect(originalEntryServerInfo *svrConnInfo, vpnProc vpn.Proc isRuning = false } case <-routingUpdateChan: // there were some routing changes but 'interfaceToProtect' is still is the default route + // If V2Ray is in use - we must update route to V2Ray server each time when default gateway IP was chnaged + if v2rayWrapper != nil { + if err := v2rayWrapper.UpdateMainRoute(); err != nil { + log.Error(err) + } + } s._vpn.OnRoutingChanged() go func() { // Ensure that current DNS configuration is correct. If not - it re-apply the required configuration. diff --git a/daemon/v2r/v2ray.go b/daemon/v2r/v2ray.go index 3f36699d..0208c167 100644 --- a/daemon/v2r/v2ray.go +++ b/daemon/v2r/v2ray.go @@ -35,6 +35,7 @@ import ( "time" "github.com/ivpn/desktop-app/daemon/logger" + "github.com/ivpn/desktop-app/daemon/netinfo" "github.com/ivpn/desktop-app/daemon/shell" ) @@ -73,6 +74,9 @@ type V2RayWrapper struct { command *exec.Cmd mutex sync.Mutex stoppedChan chan struct{} + + // IP address of the default gateway which was used for static route to V2Ray server + defaultGeteway net.IP } // CreateV2RayWrapper - creates new V2RayWrapper object @@ -147,6 +151,47 @@ func (v *V2RayWrapper) Start() error { return v.start() } +// UpdateMainRoute - updates the route to V2Ray server. +// This method must be called when the default route was changed (e.g. chnaged WiFi network) +func (v *V2RayWrapper) UpdateMainRoute() error { + v.mutex.Lock() + defer v.mutex.Unlock() + + gwIp, err := netinfo.DefaultGatewayIP() + if err != nil { + return fmt.Errorf("getting default gateway ip error : %w", err) + } + + if v.defaultGeteway != nil && v.defaultGeteway.Equal(gwIp) { + return nil //gateway did not chnage. Nothing to update + } + + log.Info("Updating route to V2Ray server...") + if err := v.deleteMainRoute(); err != nil { + log.Error(err) + } + return v.setMainRoute(gwIp) +} + +func (v *V2RayWrapper) setMainRoute(defaultGateway net.IP) error { + var err error + if defaultGateway == nil { + defaultGateway, err = netinfo.DefaultGatewayIP() + if err != nil { + return fmt.Errorf("getting default gateway ip error : %w", err) + } + } + if err := v.implSetMainRoute(defaultGateway); err == nil { + v.defaultGeteway = defaultGateway + } + return err +} + +func (v *V2RayWrapper) deleteMainRoute() error { + v.defaultGeteway = nil + return v.implDeleteMainRoute() +} + func (v *V2RayWrapper) start() (retError error) { // check if object correctly initialized if v.binary == "" { @@ -183,13 +228,13 @@ func (v *V2RayWrapper) start() (retError error) { } // Apply route to remote endpoint - if err := v.implSetMainRoute(); err != nil { + if err := v.setMainRoute(nil); err != nil { return fmt.Errorf("error applying route to remote V2Ray endpoint: %w", err) } defer func() { if retError != nil { // in case of error - ensure route is deleted - v.implDeleteMainRoute() + v.deleteMainRoute() } }() diff --git a/daemon/v2r/v2ray_darwin.go b/daemon/v2r/v2ray_darwin.go index e5f4c4f7..25a88cea 100644 --- a/daemon/v2r/v2ray_darwin.go +++ b/daemon/v2r/v2ray_darwin.go @@ -24,8 +24,8 @@ package v2r import ( "fmt" + "net" - "github.com/ivpn/desktop-app/daemon/netinfo" "github.com/ivpn/desktop-app/daemon/shell" ) @@ -33,19 +33,14 @@ func implInit() { // nothing to do here for macOS } -func (v *V2RayWrapper) implSetMainRoute() error { - gwIp, err := netinfo.DefaultGatewayIP() - if err != nil { - return fmt.Errorf("getting default gateway ip error : %w", err) - } - +func (v *V2RayWrapper) implSetMainRoute(defaultGateway net.IP) error { remoteHost, _, err := v.getRemoteEndpoint() if err != nil { return fmt.Errorf("getting remote endpoint error : %w", err) } // ip route add 144.217.233.114/32 via 192.168.0.1 dev eth0 - if err := shell.Exec(log, "/sbin/route", "-n", "add", "-inet", "-net", remoteHost.String(), gwIp.String(), "255.255.255.255"); err != nil { + if err := shell.Exec(log, "/sbin/route", "-n", "add", "-inet", "-net", remoteHost.String(), defaultGateway.String(), "255.255.255.255"); err != nil { return fmt.Errorf("adding route shell comand error : %w", err) } diff --git a/daemon/v2r/v2ray_linux.go b/daemon/v2r/v2ray_linux.go index ddf7b86f..eca6ac81 100644 --- a/daemon/v2r/v2ray_linux.go +++ b/daemon/v2r/v2ray_linux.go @@ -24,8 +24,8 @@ package v2r import ( "fmt" + "net" - "github.com/ivpn/desktop-app/daemon/netinfo" "github.com/ivpn/desktop-app/daemon/shell" ) @@ -33,19 +33,14 @@ func implInit() { // nothing to do here for macOS } -func (v *V2RayWrapper) implSetMainRoute() error { - gwIp, err := netinfo.DefaultGatewayIP() - if err != nil { - return fmt.Errorf("getting default gateway ip error : %w", err) - } - +func (v *V2RayWrapper) implSetMainRoute(defaultGateway net.IP) error { remoteHost, _, err := v.getRemoteEndpoint() if err != nil { return fmt.Errorf("getting remote endpoint error : %w", err) } // /sbin/ip route add 144.217.148.72/32 via 192.168.2.1 - if err := shell.Exec(log, "ip", "route", "add", remoteHost.String()+"/32", "via", gwIp.String()); err != nil { + if err := shell.Exec(log, "ip", "route", "add", remoteHost.String()+"/32", "via", defaultGateway.String()); err != nil { return fmt.Errorf("adding route shell comand error : %w", err) } diff --git a/daemon/v2r/v2ray_windows.go b/daemon/v2r/v2ray_windows.go index e8847869..ec0bb6fa 100644 --- a/daemon/v2r/v2ray_windows.go +++ b/daemon/v2r/v2ray_windows.go @@ -24,11 +24,11 @@ package v2r import ( "fmt" + "net" "os" "path" "strings" - "github.com/ivpn/desktop-app/daemon/netinfo" "github.com/ivpn/desktop-app/daemon/shell" ) @@ -43,23 +43,18 @@ func implInit() { } } -func (v *V2RayWrapper) implSetMainRoute() error { +func (v *V2RayWrapper) implSetMainRoute(defaultGateway net.IP) error { if routeBinaryPath == "" { return fmt.Errorf("route.exe location not specified") } - gwIp, err := netinfo.DefaultGatewayIP() - if err != nil { - return fmt.Errorf("getting default gateway ip error : %w", err) - } - remoteHost, _, err := v.getRemoteEndpoint() if err != nil { return fmt.Errorf("getting remote endpoint error : %w", err) } // route.exe add 144.217.233.114 mask 255.255.255.255 192.168.0.1 - if err := shell.Exec(log, routeBinaryPath, "add", remoteHost.String(), "mask", "255.255.255.255", gwIp.String()); err != nil { + if err := shell.Exec(log, routeBinaryPath, "add", remoteHost.String(), "mask", "255.255.255.255", defaultGateway.String()); err != nil { return fmt.Errorf("adding route shell comand error : %w", err) }