Просмотр исходного кода

Add NetworkChanged

- Generalize network change signalling for cases beyond reconnecting the
  current tunnel.

- Terminate in-proxy proxy connections on network change, for faster
  re-announce turn around time on a new network.
Rod Hynes 1 год назад
Родитель
Сommit
4fcb0daf54

+ 3 - 1
MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java

@@ -208,7 +208,9 @@ public class PsiphonTunnel {
             @Override
             @Override
             public void onChanged() {
             public void onChanged() {
                 try {
                 try {
-                    reconnectPsiphon();
+                    // networkChanged initiates a reset of all open network
+                    // connections, including a tunnel reconnect.
+                    Psi.networkChanged();
                 } catch (Exception e) {
                 } catch (Exception e) {
                     mHostService.onDiagnosticMessage("reconnect error: " + e);
                     mHostService.onDiagnosticMessage("reconnect error: " + e);
                 }
                 }

+ 7 - 4
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m

@@ -1549,11 +1549,14 @@ typedef NS_ERROR_ENUM(PsiphonTunnelErrorDomain, PsiphonTunnelErrorCode) {
 
 
         previousNetworkStatus = atomic_exchange(&self->currentNetworkStatus, networkStatus);
         previousNetworkStatus = atomic_exchange(&self->currentNetworkStatus, networkStatus);
 
 
-        // Restart if the network status or interface has changed, unless the previous status was
-        // NetworkReachabilityNotReachable, because the tunnel should be waiting for connectivity in
-        // that case.
+        // Signal when the network status or interface has changed, unless the
+        // previous status was NetworkReachabilityNotReachable, because the
+        // tunnel should be waiting for connectivity in that case.
+        //
+        // GoPsiNetworkChanged initiates a reset of all open network
+        // connections, including a tunnel reconnect.
         if ((networkStatus != previousNetworkStatus || interfaceChanged) && previousNetworkStatus != NetworkReachabilityNotReachable) {
         if ((networkStatus != previousNetworkStatus || interfaceChanged) && previousNetworkStatus != NetworkReachabilityNotReachable) {
-            GoPsiReconnectTunnel();
+            GoPsiNetworkChanged();
         }
         }
     }
     }
 }
 }

+ 12 - 0
MobileLibrary/psi/psi.go

@@ -280,6 +280,18 @@ func ReconnectTunnel() {
 	}
 	}
 }
 }
 
 
+// NetworkChanged initiates a reset of all open network connections, including
+// a tunnel reconnect.
+func NetworkChanged() {
+
+	controllerMutex.Lock()
+	defer controllerMutex.Unlock()
+
+	if controller != nil {
+		controller.NetworkChanged()
+	}
+}
+
 // SetDynamicConfig overrides the sponsor ID and authorizations fields set in
 // SetDynamicConfig overrides the sponsor ID and authorizations fields set in
 // the config passed to Start. SetDynamicConfig has no effect if no Controller
 // the config passed to Start. SetDynamicConfig has no effect if no Controller
 // is started.
 // is started.

+ 7 - 0
psiphon/common/inproxy/inproxy_test.go

@@ -91,6 +91,9 @@ func runTestInproxy(doMustUpgrade bool) error {
 	testNewTacticsTag := "new-tactics-tag"
 	testNewTacticsTag := "new-tactics-tag"
 	testUnchangedTacticsPayload := []byte(prng.HexString(100))
 	testUnchangedTacticsPayload := []byte(prng.HexString(100))
 
 
+	currentNetworkCtx, currentNetworkCancelFunc := context.WithCancel(context.Background())
+	defer currentNetworkCancelFunc()
+
 	// TODO: test port mapping
 	// TODO: test port mapping
 
 
 	stunServerAddressSucceededCount := int32(0)
 	stunServerAddressSucceededCount := int32(0)
@@ -431,6 +434,10 @@ func runTestInproxy(doMustUpgrade bool) error {
 				return true
 				return true
 			},
 			},
 
 
+			GetCurrentNetworkContext: func() context.Context {
+				return currentNetworkCtx
+			},
+
 			GetBrokerClient: func() (*BrokerClient, error) {
 			GetBrokerClient: func() (*BrokerClient, error) {
 				return brokerClient, nil
 				return brokerClient, nil
 			},
 			},

+ 16 - 0
psiphon/common/inproxy/proxy.go

@@ -87,6 +87,14 @@ type ProxyConfig struct {
 	// there is network connectivity, and false for shutdown.
 	// there is network connectivity, and false for shutdown.
 	WaitForNetworkConnectivity func() bool
 	WaitForNetworkConnectivity func() bool
 
 
+	// GetCurrentNetworkContext is a callback that returns a context tied to
+	// the lifetime of the host's current active network interface. If the
+	// active network changes, the previous context returned by
+	// GetCurrentNetworkContext should cancel. This context is used to
+	// immediately cancel/close individual connections when the active
+	// network changes.
+	GetCurrentNetworkContext func() context.Context
+
 	// GetBrokerClient provides a BrokerClient which the proxy will use for
 	// GetBrokerClient provides a BrokerClient which the proxy will use for
 	// making broker requests. If GetBrokerClient returns a shared
 	// making broker requests. If GetBrokerClient returns a shared
 	// BrokerClient instance, the BrokerClient must support multiple,
 	// BrokerClient instance, the BrokerClient must support multiple,
@@ -506,6 +514,14 @@ func (p *Proxy) proxyOneClient(
 	logAnnounce func() bool,
 	logAnnounce func() bool,
 	signalAnnounceDone func()) (bool, error) {
 	signalAnnounceDone func()) (bool, error) {
 
 
+	// Cancel/close this connection immediately if the network changes.
+	if p.config.GetCurrentNetworkContext != nil {
+		var cancelFunc context.CancelFunc
+		ctx, cancelFunc = common.MergeContextCancel(
+			ctx, p.config.GetCurrentNetworkContext())
+		defer cancelFunc()
+	}
+
 	// Do not trigger back-off unless the proxy successfully announces and
 	// Do not trigger back-off unless the proxy successfully announces and
 	// only then performs poorly.
 	// only then performs poorly.
 	//
 	//

+ 47 - 0
psiphon/controller.go

@@ -101,6 +101,10 @@ type Controller struct {
 	inproxyLastStoredTactics                time.Time
 	inproxyLastStoredTactics                time.Time
 	establishSignalForceTacticsFetch        chan struct{}
 	establishSignalForceTacticsFetch        chan struct{}
 	inproxyClientDialRateLimiter            *rate.Limiter
 	inproxyClientDialRateLimiter            *rate.Limiter
+
+	currentNetworkMutex      sync.Mutex
+	currentNetworkCtx        context.Context
+	currentNetworkCancelFunc context.CancelFunc
 }
 }
 
 
 // NewController initializes a new controller.
 // NewController initializes a new controller.
@@ -177,6 +181,18 @@ func NewController(config *Config) (controller *Controller, err error) {
 		quicTLSClientSessionCache: tls.NewLRUClientSessionCache(0),
 		quicTLSClientSessionCache: tls.NewLRUClientSessionCache(0),
 	}
 	}
 
 
+	// Initialize the current network context. This context represents the
+	// lifetime of the host's current active network interface. When
+	// Controller.NetworkChanged is called (by the Android and iOS platform
+	// code), the previous current network interface is considered to be no
+	// longer active and the corresponding current network context is canceled.
+	// Components may use currentNetworkCtx to cancel and close old network
+	// connections and quickly initiate new connections when the active
+	// interface changes.
+
+	controller.currentNetworkCtx, controller.currentNetworkCancelFunc =
+		context.WithCancel(context.Background())
+
 	// Initialize untunneledDialConfig, used by untunneled dials including
 	// Initialize untunneledDialConfig, used by untunneled dials including
 	// remote server list and upgrade downloads.
 	// remote server list and upgrade downloads.
 	controller.untunneledDialConfig = &DialConfig{
 	controller.untunneledDialConfig = &DialConfig{
@@ -411,6 +427,9 @@ func (controller *Controller) Run(ctx context.Context) {
 		controller.packetTunnelClient.Stop()
 		controller.packetTunnelClient.Stop()
 	}
 	}
 
 
+	// Cleanup current network context
+	controller.currentNetworkCancelFunc()
+
 	// All workers -- runTunnels, establishment workers, and auxilliary
 	// All workers -- runTunnels, establishment workers, and auxilliary
 	// workers such as fetch remote server list and untunneled uprade
 	// workers such as fetch remote server list and untunneled uprade
 	// download -- operate with the controller run context and will all
 	// download -- operate with the controller run context and will all
@@ -437,6 +456,33 @@ func (controller *Controller) SetDynamicConfig(sponsorID string, authorizations
 	controller.config.SetDynamicConfig(sponsorID, authorizations)
 	controller.config.SetDynamicConfig(sponsorID, authorizations)
 }
 }
 
 
+// NetworkChanged initiates a reset of all open network connections, including
+// a tunnel reconnect, if one is running, as well as terminating any in-proxy
+// proxy connections.
+func (controller *Controller) NetworkChanged() {
+
+	// Tunnels don't yet use the current network context.
+	controller.TerminateNextActiveTunnel()
+
+	controller.currentNetworkMutex.Lock()
+	defer controller.currentNetworkMutex.Unlock()
+
+	// Cancel the previous current network context, which will interrupt any
+	// operations using this context.
+	controller.currentNetworkCancelFunc()
+
+	// Create a new context for the new current network.
+	controller.currentNetworkCtx, controller.currentNetworkCancelFunc =
+		context.WithCancel(context.Background())
+}
+
+func (controller *Controller) getCurrentNetworkContext() context.Context {
+	controller.currentNetworkMutex.Lock()
+	defer controller.currentNetworkMutex.Unlock()
+
+	return controller.currentNetworkCtx
+}
+
 // TerminateNextActiveTunnel terminates the active tunnel, which will initiate
 // TerminateNextActiveTunnel terminates the active tunnel, which will initiate
 // establishment of a new tunnel.
 // establishment of a new tunnel.
 func (controller *Controller) TerminateNextActiveTunnel() {
 func (controller *Controller) TerminateNextActiveTunnel() {
@@ -2936,6 +2982,7 @@ func (controller *Controller) runInproxyProxy() {
 		Logger:                        NoticeCommonLogger(debugLogging),
 		Logger:                        NoticeCommonLogger(debugLogging),
 		EnableWebRTCDebugLogging:      debugLogging,
 		EnableWebRTCDebugLogging:      debugLogging,
 		WaitForNetworkConnectivity:    controller.inproxyWaitForNetworkConnectivity,
 		WaitForNetworkConnectivity:    controller.inproxyWaitForNetworkConnectivity,
+		GetCurrentNetworkContext:      controller.getCurrentNetworkContext,
 		GetBrokerClient:               controller.inproxyGetProxyBrokerClient,
 		GetBrokerClient:               controller.inproxyGetProxyBrokerClient,
 		GetBaseAPIParameters:          controller.inproxyGetProxyAPIParameters,
 		GetBaseAPIParameters:          controller.inproxyGetProxyAPIParameters,
 		MakeWebRTCDialCoordinator:     controller.inproxyMakeProxyWebRTCDialCoordinator,
 		MakeWebRTCDialCoordinator:     controller.inproxyMakeProxyWebRTCDialCoordinator,