Skip to content

Commit

Permalink
Enhanced monitoring for changes or removal of the V2Ray route, restor…
Browse files Browse the repository at this point in the history
…ing it if necessary.

ivpn/desktop-app-shadow#146
  • Loading branch information
stenya committed Nov 15, 2023
1 parent 12e44ad commit 524d3b6
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 4 deletions.
5 changes: 4 additions & 1 deletion daemon/netchange/net_change_detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,14 @@ func (d *Detector) DelayBeforeNotify() time.Duration {
return d.delayBeforeNotify
}

// must be called when routing change detected (called from platform-specific sources)
// Must be called when routing change detected (called from platform-specific sources)
// It notifies about routing change with delay 'd.DelayBeforeNotify()'. This reduces amount of multiple consecutive notifications
func (d *Detector) routingChangeDetected() {
d.timerNotifyAfterDelay.Reset(d.DelayBeforeNotify())
}

// Immediately notify about routing change.
// Consider using routingChangeDetected() instead
func (d *Detector) notifyRoutingChange() {
if d.routingChangeNotifyChan == nil {
return
Expand Down
2 changes: 1 addition & 1 deletion daemon/netchange/net_change_detector_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (d *Detector) doStart() {
}

// notify about routing change
d.notifyRoutingChange()
d.routingChangeDetected()
}
}

Expand Down
53 changes: 51 additions & 2 deletions daemon/v2r/v2ray.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ func init() {
implInit() // platform-specific initialisation
}

// IMPORTANT: Hardcoded string constants! The may be changed after update V2Ray version!!!
// In use for checking V2Ray console output: for connectivity issues detectin to V2Ray server
// IMPORTANT: Hardcoded string constants!
// Used to check the V2Ray process console output for detecting connectivity issues with the V2Ray server.
// Output examples:
//
// 2023/11/15 13:34:45 [Info] transport/internet/quic: dialing QUIC to udp:185.246.211.184:2049
// 2023/11/15 13:41:17 [Info] [2879864986] transport/internet/tcp: dialing TCP to tcp:185.246.211.184:80
//
// Two 'dialing' attempts during a short period (20s) may indicate a problem with connecting to the V2Ray server.
// Therefore, it is necessary to check the consistency of the route to the V2Ray server.
var detectDialingRegexp = regexp.MustCompile(".* transport/internet.* dialing .*")

const detectDialingInterval = time.Second * 20

type V2RayTransportType int

const (
Expand Down Expand Up @@ -80,16 +95,32 @@ type V2RayWrapper struct {
// IP address of local interface which is in use for communication with V2Ray server
// (we use use it to detect changes of the route to V2Ray server)
localInterfaceIp net.IP
// The time of last 'dialing' detected
// Two 'dialing' attempts during a short period (20s) may indicate a problem with connecting to the V2Ray server.
// Therefore, it is necessary to check the consistency of the route to the V2Ray server.
timeLastDialingDetected time.Time
updateRouteDelayTimer *time.Timer
}

// CreateV2RayWrapper - creates new V2RayWrapper object
// Please refer to the v2r.V2RayConfig (in v2r/config.go) struct for more information about the V2Ray data flow and configuration
func CreateV2RayWrapper(binary string, tmpConfigFile string, cfg *V2RayConfig) *V2RayWrapper {
return &V2RayWrapper{
wrapper := &V2RayWrapper{
binary: binary,
tempConfigFile: tmpConfigFile,
config: cfg,
}

// initialize 'delay'-timer for calling UpdateMainRoute() function
timer := time.AfterFunc(detectDialingInterval, func() {
if err := wrapper.UpdateMainRoute(); err != nil {
log.Error(err)
}
})
timer.Stop() // ensure timer is stopped (no changes detected for now)
wrapper.updateRouteDelayTimer = timer // save timer

return wrapper
}

func (v *V2RayWrapper) GetLocalPort() (port int, isTcp bool, err error) {
Expand Down Expand Up @@ -172,7 +203,7 @@ func (v *V2RayWrapper) UpdateMainRoute() error {
isGatewayChanged := false
gwIp, err := netinfo.DefaultGatewayIP()
if err != nil {
return fmt.Errorf("getting default gateway ip error : %w", err)
return fmt.Errorf("failed to check V2Ray route consistency: %w", err)
}
if v.defaultGeteway != nil && !v.defaultGeteway.Equal(gwIp) {
isGatewayChanged = true
Expand Down Expand Up @@ -275,6 +306,24 @@ func (v *V2RayWrapper) start() (retError error) {
// regexp to parse output and get local port number from it (if any)
portRegExp := regexp.MustCompile(`^.+\s+\[Info\]\s+transport/internet/((udp)|(tcp)):\s+listening\s+((UDP)|(TCP))\s+on\s+0\.0\.0\.0:([0-9]+)\s*$`)
outputParseFunc := func(text string, isError bool) {
// Checking inconsistency with route to V2Ray server
// Two 'dialing' attempts during a short period (20s) may indicate a problem with connecting to the V2Ray server.
// Therefore, it is necessary to check the consistency of the route to the V2Ray server.
//
// Note: This is typically necessary only for Linux implementation.
// The function UpdateMainRoute() is usually called externally by the service when changes in the routing table are detected.
// However, the Linux implementation currently lacks route-change detection functionality.
if detectDialingRegexp.FindStringSubmatchIndex(text) != nil {
now := time.Now()
if v.timeLastDialingDetected.Add(detectDialingInterval).After(now) {
v.timeLastDialingDetected = time.Time{}
// check route not ofthen than once per 5 seconds
// (do not use here long delays >= 8sec, since delay before V2Ray try to make new dial attempt for QUIC protocol is 8 sec)
v.updateRouteDelayTimer.Reset(time.Second * 5)
}
v.timeLastDialingDetected = now
}

if isError {
log.Info("[ERR] ", text)

Expand Down

0 comments on commit 524d3b6

Please sign in to comment.